Skip to content
Snippets Groups Projects
Select Git revision
  • d85cc387cfa6c49aa5d89795da892cb390c44f1b
  • master default protected
  • 9.0
  • 8.0
  • nt-tools
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2020
  • v8.0.2018
  • v8.0.2016
  • v8.0.2014
  • v8.0.2012
  • v8.0.2008
  • v8.0.2006
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
41 results

cpp.c

Blame
  • _Image_PSD.pmod 16.38 KiB
    #pike __REAL_VERSION__
    
    
    //! @appears Image.PSD
    //! PhotoShop Document image format.
    
    inherit Image._PSD;
    
    //!
    class Layer
    {
    
      string mode, name;
      //!
    
      int opacity;
      //!
    
      object image;
      //!
    
      object alpha;
      //!
    
      int flags;
      //!
    
      int xoffset, yoffset;
      //!
    
      int width, height;
      //!
    
      int mask_flags;
      //!
    
      int mask_xoffset, mask_yoffset;
      //!
    
      int mask_width, mask_height;
      //!
    
      int mask_default_color;
      //!
    }
    
    Layer decode_layer(mapping layer, mapping i)
    {
    //   int stt = gethrtime();
      Layer l = Layer();
      int use_cmap;
      l->opacity = layer->opacity;
      l->width = layer->right-layer->left;
      l->height = layer->bottom-layer->top;
      l->xoffset = layer->left;
      l->yoffset = layer->top;
      l->image = Image.Image( l->width, l->height );
      l->mode = layer->mode;
      l->flags = layer->flags;
      l->name = layer->name;
      l->mask_flags = layer->mask_flags;
      l->mask_default_color = layer->mask_default_color;
     
      l->mask_width = layer->mask_right-layer->mask_left;
      l->mask_height = layer->mask_bottom-layer->mask_top;
      l->mask_xoffset = layer->mask_left;
      l->mask_yoffset = layer->mask_top;
    
      if(l->mask_flags & 1) // pos relative to layer
      {
        l->mask_xoffset += l->xoffset;
        l->mask_yoffset += l->yoffset;
      }
      array colors;
      int inverted;
    
      switch(i->mode)
      {
       case RGB:
         array lays = ({});
         foreach( layer->channels, mapping c )
         {
           string mode;
           switch( (int)c->id )
           {
            case 0:
              mode = "red"; 
              break;
            case 1:
              mode = "green"; 
              break;
            case 2:
              mode = "blue"; 
              break;
           }
           if( mode )
           {
    //          int st = gethrtime();
             if( !sizeof(lays) )
               lays += ({ 
                 Image.Layer(___decode_image_channel(l->width,
    						 l->height,
                                                     c->data))
               });
             else
               lays += (({ Image.Layer( ([
                 "image":___decode_image_channel(l->width, l->height, c->data),
    //             "alpha_value":1.0,
                 "mode":mode, 
    	   ]) )
               }));
    //          werror(mode+" took %4.5f seconds\n", (gethrtime()-st)/1000000.0 );
             c->data = 0;
           }
         }
    //      int st = gethrtime();
         l->image = Image.lay( lays )->image();
    //      werror("combine took %4.5f seconds\n", (gethrtime()-st)/1000000.0 );
         break;
        case CMYK:
         inverted = 1;
         colors = ({ ({255,0,0,}),
                     ({0,255,0,}),
                     ({0,0,255,}),
                   }) + ({ 255,255,255 }) * 24;
         l->image = Image.Image( l->width, l->height, 255, 255, 255);
         break;
    
       case Indexed:
         use_cmap = 1;
         break;
       default:
         werror("Unsupported layer format mode ("+i->mode+"), using greyscale\n");
       case Greyscale:
         colors = ({
           255,255,255
         })*24;
         break;
      }
    //   int st = gethrtime();
      foreach(layer->channels, mapping c)
      {
        object tmp;
    
        if( !colors && (c->id >= 0 ))
          continue;
    
        if( c->id != -2)
          tmp = ___decode_image_channel(l->width, l->height, c->data);
        else
          tmp = ___decode_image_channel(l->mask_width,l->mask_height,c->data);
    
        switch( c->id )
        {
         default:
           if(!use_cmap)
           {
             if(inverted)
               l->image -= tmp*colors[c->id%sizeof(colors)];
             else
               l->image += tmp*colors[c->id%sizeof(colors)];
           } else {
             __apply_cmap( tmp, i->color_data );
             l->image = tmp;
           }
           break;
         case -1: /* alpha */
           if(!l->alpha)
             l->alpha = tmp;
           else
             l->alpha *= tmp;
           break;
         case -2: /* user mask */
           if(!(l->mask_flags & 2)) /* layer mask disabled */
           {
    	 array pad_color = ({ l->mask_default_color }) * 3;
    	 int x0 = l->xoffset - l->mask_xoffset;
    	 int y0 = l->yoffset - l->mask_yoffset;
    	 tmp = tmp->copy(x0, y0,
    			 x0 + l->image->xsize() - 1,
    			 y0 + l->image->ysize() - 1,
    			 @pad_color);
    	 
             if(l->mask_flags & 4) /* invert mask */
               tmp = tmp->invert();
    	 
             if(!l->alpha)
               l->alpha = tmp;
             else
               l->alpha *= tmp;
             break;
           }
        }
        c->data = 0;
      }
    //   werror(" mode %s image %O alpha %O\n",
    // 	 l->mode, l->image, l->alpha );
    //   werror("alpha/mask took %4.5f seconds\n", (gethrtime()-st)/1000000.0 );
    //   werror("TOTAL took %4.5f seconds\n\n", (gethrtime()-stt)/1000000.0 );
      return l;
    }
    
    //! @decl mapping __decode(string|mapping data)
    //! 
    //! Decodes a PSD image to a mapping, defined as follows.
    //!
    //! @mapping
    //!   @member int(1..24) "channels"
    //!     The number of channels in the image, including any alpha channels.
    //!   @member int(1..30000) "height"
    //!   @member int(1..30000) "width"
    //!     The image dimensions.
    //!   @member int(0..1) "compression"
    //!     1 if the image is compressed, 0 if not.
    //!   @member int(1..1)|int(8..8)|int(16..16) "depth"
    //!     The number of bits per channel.
    //!   @member int(0..4)|int(7..9) "mode"
    //!     The color mode of the file.
    //!     @int
    //!       @value 0
    //!         Bitmap
    //!       @value 1
    //!         Greyscale
    //!       @value 2
    //!         Indexed
    //!       @value 3
    //!         RGB
    //!       @value 4
    //!         CMYK
    //!       @value 7
    //!         Multichannel
    //!       @value 8
    //!         Duotone
    //!       @value 9
    //!         Lab
    //!     @endint
    //!   @member string "color_data"
    //!     Raw color data.
    //!   @member string "image_data"
    //!     Ram image data.
    //!   @member  mapping(string|int:mixed) "resources"
    //!     Additional image data. See mappping below.
    //!   @member array(Layer) "layers"
    //!     An array with the layers of the image. See mapping below.
    //! @endmapping
    //!
    //! The resources mapping. Unknown resources will be identified
    //! by their ID number (as an int).
    //! @mapping
    //!   @member string "caption"
    //!     Image caption.
    //!   @member string "url"
    //!     Image associated URL.
    //!   @member int "active_layer"
    //!     Which layer is active.
    //!   @member array(mapping(string:int)) "guides"
    //!     An array with all guides stored in the image file.
    //!     @mapping
    //!       @member int "pos"
    //!         The position of the guide.
    //!       @member int(0..1) "vertical"
    //!         1 if the guide is vertical, 0 if it is horizontal.
    //!     @endmapping
    //!   @member mapping(string:int) "resinfo"
    //!     Resolution information
    //!     @mapping
    //!       @member int "hres"
    //!       @member int "hres_unit"
    //!       @member int "width_unit"
    //!       @member int "vres"
    //!       @member int "vres_unit"
    //!       @member int "height_unit"
    //!         FIXME: Document these.
    //!     @endmapping
    //! @endmapping
    //!
    //! The layer members:
    //! @mapping
    //!   @member int "top"
    //!   @member int "left"
    //!   @member int "right"
    //!   @member int "bottom"
    //!     The rectangle containing the contents of the layer.
    //!   @member int "mask_top"
    //!   @member int "mask_left"
    //!   @member int "mask_right"
    //!   @member int "mask_bottom"
    //!   @member int "mask_flags"
    //!     FIXME: Document these
    //!   @member int(0..255) "opacity"
    //!     0=transparent, 255=opaque.
    //!   @member int "clipping"
    //!     0=base, 1=non-base.
    //!   @member int "flags"
    //!     bit 0=transparency protected
    //!     bit 1=visible
    //!   @member string "mode"
    //!     Blend mode.
    //!     @string
    //!       @value "norm"
    //!         Normal
    //!       @value "dark"
    //!         Darken
    //!       @value "lite"
    //!         Lighten
    //!       @value "hue "
    //!         Hue
    //!       @value "sat "
    //!         Saturation
    //!       @value "colr"
    //!         Color
    //!       @value "lum "
    //!         Luminosity
    //!       @value "mul "
    //!         Multiply
    //!       @value "scrn"
    //!         Screen
    //!       @value "diss"
    //!         Dissolve
    //!       @value "over"
    //!         Overlay
    //!       @value "hLit"
    //!         Hard light
    //!       @value "sLit"
    //!         Soft light
    //!       @value "diff"
    //!         Difference
    //!     @endstring
    //!   @member string "extra_data"
    //!     Raw extra data.
    //!   @member string "name"
    //!     The name of the layer
    //!   @member array(mapping(string:int|string)) "channels"
    //!     The channels of the layer. Each array element is a
    //!     mapping as follows
    //!     @mapping
    //!       @member int "id"
    //!         The ID of the channel
    //!         @int
    //!           @value -2
    //!             User supplied layer mask
    //!           @value -1
    //!             Transparency mask
    //!           @value 0
    //!             Red
    //!           @value 1
    //!             Green
    //!           @value 2
    //!             Blue
    //!         @endint
    //!       @member string "data"
    //!         The image data
    //!     @endmapping
    //! @endmapping
    mapping __decode( mapping|string what, mapping|void options )
    {
      mapping data;
      if(mappingp(what))
        data = what;
      else 
        data = ___decode( what );
      what=0;
      array rl = ({});
      foreach( data->layers, mapping l )
        rl += ({ decode_layer( l, data ) });
    
      data->layers = rl;
      return data;
    }
    
    array(object) decode_background( mapping data )
    {
      object img;
    
      if( data->image_data )
        img = ___decode_image_data(data->width,       data->height, 
                                   data->channels,    data->mode,
                                   data->compression, data->image_data,
    
                                   data->color_data);
      return ({ img, 0 });
    }
    
    //! Convert a photoshop mode name to pike @[Image.lay] mode names
    string translate_mode( string mode )
    {
      switch( mode )
      {
       case "norm":      return "normal";
       case "mul ":      return "multiply";
       case "add ":      return "add";
       case "diff":      return "difference";
       case "sub ":      return "subtract";
       case "diss":      return "dissolve";
       case "scrn":      return "screen";
       case "over":      return "overlay";
       case "dark":      return "min";
       case "lite":      return "max";
       case "hue ":      return "hue";
       case "div ":      return "idivide";
       case "hLit":      return "hardlight";
    
       case "colr":      return "color"; // Not 100% like photoshop.
       case "lum ":      return "value"; // Not 100% like photoshop.
       case "sat ":      return "saturation";// Not 100% like photoshop.
    
          // Colorburn, not really multiply, but the best aproximation soo far.
        case "idiv":
         return "multiply";
    
         // Exclusion. Not really difference, but very close.
        case "smud":     
         return "difference";
    
         // Soft light. Not really supported yet. For now, use hardlight with lower
         // opacity. Gives a rather good aproximation.
        case "sLit":     
         return "hardlight";
    
       default:
         werror("WARNING: PSD: Unsupported mode: "+mode+". Skipping layer\n");
         return 0;
      }
    }
    
    //! @decl array(Image.Layer) decode_layers( string data, mapping|void options )
    //!
    //! Decodes a PSD image to an array of Image.Layer objects
    //!
    //! The layer object have the following extra variables (to be queried
    //! using @[Image.Layer()->get_misc_value]):
    //!
    //! @string
    //!   @value "image_guides"
    //!     Returns array containing guide definitions.
    //!   @value "name"
    //!     Returns string containing the name of the layer.
    //!   @value "visible"
    //!     Is 1 of the layer is visible and 0 if it is hidden.
    //! @endstring
    //!
    //! Accepts these options:
    //! @mapping
    //! @member bool "draw_all_layers"
    //!   If included, all layers will be decoded, even the non-visible ones.
    //! @member bool "crop_to_bounds"
    //!   Remove areas that are outside the image boundaries in all layers
    //! @member Image.Color "background"
    //!   If included, include a solid background layer with the given color
    //! @endmapping
    array decode_layers( string|mapping what, mapping|void opts )
    {
      if(!opts) opts = ([]);
    
      if(!mappingp( what ) )
        what = __decode( what );
      
      mapping lopts = ([ "tiled":1, ]);
    
      if( opts->background )
      {
        lopts->image = Image.Image( 32, 32, opts->background );
        lopts->alpha = Image.Image( 32, 32, Image.Color.white );
        lopts->alpha_value = 1.0;
      }
    
      object img, alpha;
      if( !what->layers || !sizeof(what->layers))
      {
        [ img, alpha ] = decode_background( what );
        if( img )
        {
          lopts->image = img;
          if( alpha )
            lopts->alpha = alpha;
          else
            lopts->alpha = 0;
          lopts->alpha_value = 1.0;
        }
      }
      array layers;
      Image.Layer lay;
      if( lopts->image )
      {
        layers = ({ (lay=Image.Layer( lopts )) });
        lay->set_misc_value( "visible", 1 );
        lay->set_misc_value( "name",    "Background" );
        lay->set_misc_value( "image_guides", what->resources->guides );
      }
      else
        layers = ({});
    
      foreach(reverse(what->layers), object l)
      {
        if( !(l->flags & LAYER_FLAG_INVISIBLE) || opts->draw_all_layers )
        {
          if( string m = translate_mode( l->mode ) )
          {
    	lay = Image.Layer( l->image, l->alpha, m );
    
            lay->set_misc_value( "visible", !(l->flags & LAYER_FLAG_INVISIBLE) );
            lay->set_misc_value( "name",      l->name );
            lay->set_misc_value( "image_guides", what->resources->guides );
    	if( l->mode == "sLit" )
    	  l->opacity /= 3;
    
    	l->image = 0; l->alpha = 0;
            
    	if( l->opacity != 255 )
            {
    	  float lo =  l->opacity / 255.0;
              if( lay->alpha() )
                lay->set_image( lay->image(), lay->alpha()*lo );
              else
                lay->set_image( lay->image(), Image.Image( lay->xsize(),
                                                           lay->yszize(),
                                                           (int)(255*lo),
                                                           (int)(255*lo),
                                                           (int)(255*lo)));
            }
    
    	if (opts->crop_to_bounds && lay->image()) {
    	  //  Crop/expand this layer so it matches the image bounds.
    	  //  This will lose data which extends beyond the image bounds
    	  //  but keeps the image dimensions consistent.
    	  int x0 = -l->xoffset, y0 = -l->yoffset;
    	  int x1 = x0 + what->width - 1, y1 = y0 + what->height - 1;
    	  Image.Image new_img =
    	    lay->image()->copy(x0, y0, x1, y1);
    	  Image.Image new_alpha =
    	    lay->alpha() &&
    	    lay->alpha()->copy(x0, y0, x1, y1);
    	  lay->set_image(new_img, new_alpha);
    	} else
    	  lay->set_offset( l->xoffset, l->yoffset );
    
    	layers += ({ lay });
          }
        }
      }
    //   werror("%O\n", layers );
      layers->set_misc_value( "size", ({ what->width, what->height }) );
      return layers;
    }
    
    //! @decl mapping _decode(string|mapping data, mapping|void options)
    //!
    //! Decodes a PSD image to a mapping, with at least an
    //! 'image' and possibly an 'alpha' object. Data is either a PSD image, or
    //! a mapping (as received from @[__decode])
    //!
    //! @param options
    //! @mapping
    //!   @member array(int)|Image.Color "background"
    //!     Sets the background to the given color. Arrays should be in
    //!     the format ({r,g,b}).
    //!
    //!   @member bool "draw_all_layers"
    //!     Draw invisible layers as well.
    //!
    //!   @member bool "draw_guides"
    //!     Draw the guides.
    //!
    //!   @member bool "draw_selection"
    //!     Mark the selection using an overlay.
    //!
    //!   @member bool "ignore_unknown_layer_modes"
    //!     Do not asume 'Normal' for unknown layer modes.
    //!
    //!   @member bool "mark_layers"
    //!     Draw an outline around all (drawn) layers.
    //!
    //!   @member Image.Font "mark_layer_names"
    //!     Write the name of all layers using the font object,
    //!
    //!   @member bool "mark_active_layer"
    //!     Draw an outline around the active layer
    //! @endmapping
    //!
    //! @returns
    //! @mapping
    //!   @member Image.Image "image"
    //!     The image object.
    //!   @member Image.Image "alpha"
    //!     The alpha channel image object.
    //! @endmapping
    //!
    //! @note
    //!   Throws upon error in data. For more information, see @[__decode]
    mapping _decode( string|mapping what, mapping|void opts )
    {
      mapping data;
      if(!opts) opts = ([]);
      if(mappingp(what))
        data = what;
      else 
        data = __decode( what );
      what=0;
    
      Image.Layer res = Image.lay(decode_layers( data, opts ),
                                  0,0,data->width,data->height );
      Image.Image img = res->image();
      Image.Image alpha = res->alpha();
    
      return 
      ([
        "image":img,
        "alpha":alpha,
        "type":"application/x-photoshop",
      ]);
    }
    
    //! @decl Image.Image decode(string data)
    //!   Decodes a PSD image to a single image object.
    //!
    //! @note
    //!   Throws upon error in data. To get access to more information like
    //!   alpha channels and layer names, see @[_decode] and @[__decode].
    Image.Image decode( string|mapping what, mapping|void opts )
    {
      mapping data;
      if(!opts) opts = ([]);
      if(mappingp(what))
        data = what;
      else 
        data = __decode( what );
      what=0;
    
      Image.Layer res = Image.lay(decode_layers( data, opts ),
                                  0,0,data->width,data->height );
      return res->image();
    }