mattermost-community-enterp.../vendor/github.com/oov/psd/psd.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

314 lines
8.2 KiB
Go

package psd
import (
"errors"
"image"
"image/color"
"io"
)
// logger is subset of log.Logger.
type logger interface {
Printf(format string, v ...interface{})
Println(v ...interface{})
}
// Debug is useful for debugging.
//
// You can use by performing the following steps.
// psd.Debug = log.New(os.Stdout, "psd: ", log.Lshortfile)
var Debug logger
const (
headerSignature = "8BPS"
sectionSignature = "8BIM"
)
// AdditionalInfoKey represents the key of the additional layer information.
type AdditionalInfoKey string
// LenSize returns bytes of the length for this key.
func (a AdditionalInfoKey) LenSize(largeDocument bool) int {
if largeDocument {
// http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_71546
// (**PSB**, the following keys have a length count of 8 bytes:
// LMsk, Lr16, Lr32, Layr, Mt16, Mt32, Mtrn, Alph, FMsk, lnk2, FEid, FXid, PxSD.
switch a {
case "LMsk",
"Lr16",
"Lr32",
"Layr",
"Mt16",
"Mt32",
"Mtrn",
"Alph",
"FMsk",
"lnk2",
"FEid",
"FXid",
"PxSD":
return 8
}
}
return 4
}
// These keys are defined in this document.
//
// http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_71546
const (
AdditionalInfoKeyLayerInfo = AdditionalInfoKey("Layr")
AdditionalInfoKeyLayerInfo16 = AdditionalInfoKey("Lr16")
AdditionalInfoKeyLayerInfo32 = AdditionalInfoKey("Lr32")
AdditionalInfoKeyUnicodeLayerName = AdditionalInfoKey("luni")
AdditionalInfoKeyBlendClippingElements = AdditionalInfoKey("clbl")
AdditionalInfoKeySectionDividerSetting = AdditionalInfoKey("lsct")
AdditionalInfoKeySectionDividerSetting2 = AdditionalInfoKey("lsdk")
)
// Config represents Photoshop image file configuration.
type Config struct {
Version int
Rect image.Rectangle
Channels int
Depth int // 1 or 8 or 16 or 32
ColorMode ColorMode
ColorModeData []byte
Res map[int]ImageResource
}
// PSB returns whether image is large document format.
func (cfg *Config) PSB() bool {
return cfg.Version == 2
}
// Palette returns Palette if any.
func (cfg *Config) Palette() color.Palette {
if cfg.ColorMode != ColorModeIndexed {
return nil
}
// http://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_38034
// 0x0417(1046) | (Photoshop 6.0) Transparency Index.
// 2 bytes for the index of transparent color, if any.
transparentColorIndex := -1
if r, ok := cfg.Res[0x0417]; ok {
transparentColorIndex = int(readUint16(r.Data, 0))
}
pal := make(color.Palette, len(cfg.ColorModeData)/3)
for i := range pal {
c := color.NRGBA{
cfg.ColorModeData[i],
cfg.ColorModeData[i+256],
cfg.ColorModeData[i+512],
0xFF,
}
if i == transparentColorIndex {
c.A = 0
}
pal[i] = c
}
return pal
}
func (cfg *Config) makePicker() (picker, error) {
var pk picker
switch cfg.ColorMode {
case ColorModeIndexed:
pk = &pickerPalette{Palette: cfg.Palette()}
case ColorModeBitmap:
pk = &pickerGray1{}
default:
pk = findPicker(cfg.Depth, cfg.ColorMode, cfg.Channels > cfg.ColorMode.Channels() && hasAlphaID0(cfg.Res))
}
if pk == nil {
return nil, errors.New("psd: color mode " + itoa(int(cfg.ColorMode)) + " depth " + itoa(cfg.Depth) + " is not implemeted")
}
return pk, nil
}
// PSD represents Photoshop image file.
type PSD struct {
Config Config
Channel map[int]Channel
Layer []Layer
AdditinalLayerInfo map[AdditionalInfoKey][]byte
// Data is uncompressed merged image data.
Data []byte
Picker image.Image
}
// DecodeConfig returns the color model and dimensions of a image without decoding the entire image.
func DecodeConfig(r io.Reader) (cfg Config, read int, err error) {
const (
fileHeaderLen = 4 + 2 + 6 + 2 + 4 + 4 + 2 + 2
colorModeDataLen = 4
)
b := make([]byte, fileHeaderLen+colorModeDataLen)
var l int
if l, err = io.ReadFull(r, b); err != nil {
return Config{}, 0, err
}
read += l
if string(b[:4]) != headerSignature { // Signature
return Config{}, read, errors.New("psd: invalid format")
}
cfg.Version = int(readUint16(b, 4))
if cfg.Version != 1 && cfg.Version != 2 { // Version
return Config{}, read, errors.New("psd: unexpected file version: " + itoa(cfg.Version))
}
// b[6:12] Reserved
cfg.Channels = int(readUint16(b, 12))
if cfg.Channels < 1 || cfg.Channels > 56 {
return Config{}, read, errors.New("psd: unexpected the number of channels")
}
width, height := int(readUint32(b, 18)), int(readUint32(b, 14))
if height < 1 || (cfg.Version == 1 && height > 30000) || (cfg.Version == 2 && height > 300000) {
return Config{}, read, errors.New("psd: unexpected the height of the image")
}
if width < 1 || (cfg.Version == 1 && width > 30000) || (cfg.Version == 2 && width > 300000) {
return Config{}, read, errors.New("psd: unexpected the width of the image")
}
cfg.Rect = image.Rect(0, 0, width, height)
cfg.Depth = int(readUint16(b, 22))
if cfg.Depth != 1 && cfg.Depth != 8 && cfg.Depth != 16 && cfg.Depth != 32 {
return Config{}, read, errors.New("psd: unexpected color depth")
}
cfg.ColorMode = ColorMode(readUint16(b, 24))
if ln := readUint32(b, 26); ln > 0 {
cfg.ColorModeData = make([]byte, ln)
if l, err = io.ReadFull(r, cfg.ColorModeData); err != nil {
return Config{}, read, err
}
read += l
}
if Debug != nil {
Debug.Println("start - header")
Debug.Printf(" width: %d height: %d", width, height)
Debug.Printf(" channels: %d depth: %d colorMode %d", cfg.Channels, cfg.Depth, cfg.ColorMode)
Debug.Printf(" colorModeDataLen: %d", len(cfg.ColorModeData))
Debug.Println("end - header")
}
if cfg.Res, l, err = readImageResource(r); err != nil {
return Config{}, read, err
}
read += l
return cfg, read, nil
}
// DecodeOptions are the decoding options.
type DecodeOptions struct {
SkipLayerImage bool
SkipMergedImage bool
ConfigLoaded func(cfg Config) error
LayerImageLoaded func(layer *Layer, index int, total int)
}
// Decode reads a image from r and returns it.
// Default parameters are used if a nil *DecodeOptions is passed.
func Decode(r io.Reader, o *DecodeOptions) (psd *PSD, read int, err error) {
if o == nil {
o = &DecodeOptions{}
}
cfg, l, err := DecodeConfig(r)
if err != nil {
return nil, 0, err
}
read += l
if o.ConfigLoaded != nil {
err = o.ConfigLoaded(cfg)
if err != nil {
return nil, 0, err
}
}
psd, l, err = readLayerAndMaskInfo(r, &cfg, o)
if err != nil {
return nil, read, err
}
read += l
if !o.SkipMergedImage {
// allocate image buffer.
// `+7` and `>>3` is the padding for the 1bit depth color mode.
plane := (psd.Config.Rect.Dx()*psd.Config.Depth + 7) >> 3 * psd.Config.Rect.Dy()
psd.Data = make([]byte, plane*psd.Config.Channels)
chs := make([][]byte, psd.Config.Channels)
psd.Channel = make(map[int]Channel)
for i := 0; i < psd.Config.Channels; i++ {
data := psd.Data[plane*i : plane*(i+1) : plane*(i+1)]
chs[i] = data
pk := findGrayPicker(psd.Config.Depth)
pk.setSource(psd.Config.Rect, data)
psd.Channel[i] = Channel{
Data: data,
Picker: pk,
}
}
var pk picker
if pk, err = cfg.makePicker(); err != nil {
return nil, read, err
}
pk.setSource(psd.Config.Rect, chs...)
psd.Picker = pk
b := make([]byte, 2)
if l, err = io.ReadFull(r, b[:2]); err != nil {
return nil, read, err
}
read += l
cmpMethod := CompressionMethod(readUint16(b, 0))
l, err = cmpMethod.Decode(
psd.Data,
r,
0,
psd.Config.Rect,
psd.Config.Depth,
psd.Config.Channels,
psd.Config.PSB(),
)
if err != nil {
return nil, read, err
}
read += l
}
return psd, read, nil
}
func decode(r io.Reader) (image.Image, error) {
psd, _, err := Decode(r, &DecodeOptions{SkipLayerImage: true})
if err != nil {
return nil, err
}
return psd.Picker, nil
}
func decodeConfig(r io.Reader) (image.Config, error) {
cfg, _, err := DecodeConfig(r)
if err != nil {
return image.Config{}, err
}
var pk picker
if pk, err = cfg.makePicker(); err != nil {
return image.Config{}, err
}
return image.Config{
Width: cfg.Rect.Dx(),
Height: cfg.Rect.Dy(),
ColorModel: pk.ColorModel(),
}, nil
}
func init() {
image.RegisterFormat("psd", headerSignature+"\x00\x01", decode, decodeConfig)
image.RegisterFormat("psb", headerSignature+"\x00\x02", decode, decodeConfig)
}