Skip to content

Commit

Permalink
Improved CS handling with Separation, Lab color ranges.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gunnsteinn Hall authored and Gunnsteinn Hall committed Aug 11, 2017
1 parent e970ac4 commit 144fa3f
Showing 1 changed file with 90 additions and 13 deletions.
103 changes: 90 additions & 13 deletions pdf/model/colorspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ type PdfColorspace interface {
ToPdfObject() PdfObject
ColorFromPdfObjects(objects []PdfObject) (PdfColor, error)
ColorFromFloats(vals []float64) (PdfColor, error)

// Returns the decode array for the CS, i.e. the range of each component.
DecodeArray() []float64
}

type PdfColor interface {
Expand Down Expand Up @@ -225,6 +228,11 @@ func (this *PdfColorspaceDeviceGray) GetNumComponents() int {
return 1
}

// DecodeArray returns the range of color component values in DeviceGray colorspace.
func (this *PdfColorspaceDeviceGray) DecodeArray() []float64 {
return []float64{0, 1.0}
}

func (this *PdfColorspaceDeviceGray) ToPdfObject() PdfObject {
return MakeName("DeviceGray")
}
Expand Down Expand Up @@ -356,6 +364,11 @@ func (this *PdfColorspaceDeviceRGB) GetNumComponents() int {
return 3
}

// DecodeArray returns the range of color component values in DeviceRGB colorspace.
func (this *PdfColorspaceDeviceRGB) DecodeArray() []float64 {
return []float64{0.0, 1.0, 0.0, 1.0, 0.0, 1.0}
}

func (this *PdfColorspaceDeviceRGB) ToPdfObject() PdfObject {
return MakeName("DeviceRGB")
}
Expand Down Expand Up @@ -497,6 +510,11 @@ func (this *PdfColorspaceDeviceCMYK) GetNumComponents() int {
return 4
}

// DecodeArray returns the range of color component values in DeviceCMYK colorspace.
func (this *PdfColorspaceDeviceCMYK) DecodeArray() []float64 {
return []float64{0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0}
}

func (this *PdfColorspaceDeviceCMYK) ToPdfObject() PdfObject {
return MakeName("DeviceCMYK")
}
Expand Down Expand Up @@ -682,6 +700,11 @@ func (this *PdfColorspaceCalGray) GetNumComponents() int {
return 1
}

// DecodeArray returns the range of color component values in CalGray colorspace.
func (this *PdfColorspaceCalGray) DecodeArray() []float64 {
return []float64{0.0, 1.0}
}

func newPdfColorspaceCalGrayFromPdfObject(obj PdfObject) (*PdfColorspaceCalGray, error) {
cs := NewPdfColorspaceCalGray()

Expand Down Expand Up @@ -958,6 +981,11 @@ func (this *PdfColorspaceCalRGB) GetNumComponents() int {
return 3
}

// DecodeArray returns the range of color component values in CalRGB colorspace.
func (this *PdfColorspaceCalRGB) DecodeArray() []float64 {
return []float64{0.0, 1.0, 0.0, 1.0, 0.0, 1.0}
}

func newPdfColorspaceCalRGBFromPdfObject(obj PdfObject) (*PdfColorspaceCalRGB, error) {
cs := NewPdfColorspaceCalRGB()

Expand Down Expand Up @@ -1272,6 +1300,21 @@ func (this *PdfColorspaceLab) GetNumComponents() int {
return 3
}

// DecodeArray returns the range of color component values in the Lab colorspace.
func (this *PdfColorspaceLab) DecodeArray() []float64 {
// Range for L
decode := []float64{0, 100}

// Range for A,B specified by range or default
if this.Range != nil && len(this.Range) == 4 {
decode = append(decode, this.Range...)
} else {
decode = append(decode, -100, 100, -100, 100)
}

return decode
}

// require parameters?
func NewPdfColorspaceLab() *PdfColorspaceLab {
cs := &PdfColorspaceLab{}
Expand Down Expand Up @@ -1526,8 +1569,9 @@ func (this *PdfColorspaceLab) ImageToRGB(img Image) (Image, error) {

componentRanges := img.decode
if len(componentRanges) != 6 {
common.Log.Debug("Error: Image in CS Lab with len(Decode) != 6 (%d)", len(componentRanges))
return rgbImage, errors.New("Range check error")
// If image's Decode not appropriate, fall back to default decode array.
common.Log.Trace("Image - Lab Decode range != 6... use [0 100 amin amax bmin bmax] default decode array")
componentRanges = this.DecodeArray()
}

samples := img.GetSamples()
Expand All @@ -1540,11 +1584,6 @@ func (this *PdfColorspaceLab) ImageToRGB(img Image) (Image, error) {
ANorm := float64(samples[i+1]) / maxVal
BNorm := float64(samples[i+2]) / maxVal

// TODO: Fall back to this.Range if img.decode not specified or erroneous?
//if this.Range != nil {
// Add the range for L which is not defined in CS's Range but is always 0-100.
// componentRanges := append([]float64{0, 100}, componentRanges...)
//}
LStar := interpolate(LNorm, 0.0, 1.0, componentRanges[0], componentRanges[1])
AStar := interpolate(ANorm, 0.0, 1.0, componentRanges[2], componentRanges[3])
BStar := interpolate(BNorm, 0.0, 1.0, componentRanges[4], componentRanges[5])
Expand Down Expand Up @@ -1641,6 +1680,11 @@ func (this *PdfColorspaceICCBased) GetNumComponents() int {
return this.N
}

// DecodeArray returns the range of color component values in the ICCBased colorspace.
func (this *PdfColorspaceICCBased) DecodeArray() []float64 {
return this.Range
}

func (this *PdfColorspaceICCBased) String() string {
return "ICCBased"
}
Expand Down Expand Up @@ -1919,6 +1963,11 @@ func (this *PdfColorspaceSpecialPattern) GetNumComponents() int {
return this.UnderlyingCS.GetNumComponents()
}

// DecodeArray returns an empty slice as there are no components associated with pattern colorspace.
func (this *PdfColorspaceSpecialPattern) DecodeArray() []float64 {
return []float64{}
}

func newPdfColorspaceSpecialPatternFromPdfObject(obj PdfObject) (*PdfColorspaceSpecialPattern, error) {
common.Log.Trace("New Pattern CS from obj: %s %T", obj.String(), obj)
cs := NewPdfColorspaceSpecialPattern()
Expand Down Expand Up @@ -2082,6 +2131,11 @@ func (this *PdfColorspaceSpecialIndexed) GetNumComponents() int {
return 1
}

// DecodeArray returns the component range values for the Indexed colorspace.
func (this *PdfColorspaceSpecialIndexed) DecodeArray() []float64 {
return []float64{0, float64(this.HiVal)}
}

func newPdfColorspaceSpecialIndexedFromPdfObject(obj PdfObject) (*PdfColorspaceSpecialIndexed, error) {
cs := NewPdfColorspaceSpecialIndexed()

Expand Down Expand Up @@ -2308,6 +2362,11 @@ func (this *PdfColorspaceSpecialSeparation) GetNumComponents() int {
return 1
}

// DecodeArray returns the component range values for the Separation colorspace.
func (this *PdfColorspaceSpecialSeparation) DecodeArray() []float64 {
return []float64{0, 1.0}
}

// Object is an array or indirect object containing the array.
func newPdfColorspaceSpecialSeparationFromPdfObject(obj PdfObject) (*PdfColorspaceSpecialSeparation, error) {
cs := NewPdfColorspaceSpecialSeparation()
Expand Down Expand Up @@ -2428,6 +2487,8 @@ func (this *PdfColorspaceSpecialSeparation) ColorToRGB(color PdfColor) (PdfColor
return this.AlternateSpace.ColorToRGB(color)
}

// ImageToRGB converts an image with samples in Separation CS to an image with samples specified in
// DeviceRGB CS.
func (this *PdfColorspaceSpecialSeparation) ImageToRGB(img Image) (Image, error) {
altImage := img

Expand All @@ -2438,6 +2499,8 @@ func (this *PdfColorspaceSpecialSeparation) ImageToRGB(img Image) (Image, error)
common.Log.Trace("samples in: %d", len(samples))
common.Log.Trace("TintTransform: %+v", this.TintTransform)

altDecode := this.AlternateSpace.DecodeArray()

altSamples := []uint32{}
// Convert tints to color data in the alternate colorspace.
for i := 0; i < len(samples); i++ {
Expand All @@ -2446,25 +2509,29 @@ func (this *PdfColorspaceSpecialSeparation) ImageToRGB(img Image) (Image, error)

// Convert the tint value to the alternate space value.
outputs, err := this.TintTransform.Evaluate([]float64{tint})
//common.Log.Trace("Converting tint value: %f -> [% f]", tint, outputs)
//common.Log.Trace("%v Converting tint value: %f -> [% f]", this.AlternateSpace, tint, outputs)

if err != nil {
return img, err
}

for _, val := range outputs {
// Clip.
val = math.Min(math.Max(0, val), 1.0)
for i, val := range outputs {
// Convert component value to 0-1 range.
altVal := interpolate(val, altDecode[i*2], altDecode[i*2+1], 0, 1)

// Rescale to [0, maxVal]
altComponent := uint32(val * maxVal)
altSamples = append(altSamples, altComponent)
altComponent := uint32(altVal * maxVal)

altSamples = append(altSamples, altComponent)
}
}
common.Log.Trace("Samples out: %d", len(altSamples))
altImage.SetSamples(altSamples)
altImage.ColorComponents = this.AlternateSpace.GetNumComponents()

// Set the image's decode parameters for interpretation in the alternative CS.
altImage.decode = altDecode

// Convert to RGB via the alternate colorspace.
return this.AlternateSpace.ImageToRGB(altImage)
}
Expand Down Expand Up @@ -2499,6 +2566,16 @@ func (this *PdfColorspaceDeviceN) GetNumComponents() int {
return len(*this.ColorantNames)
}

// DecodeArray returns the component range values for the DeviceN colorspace.
// [0 1.0 0 1.0 ...] for each color component.
func (this *PdfColorspaceDeviceN) DecodeArray() []float64 {
decode := []float64{}
for i := 0; i < this.GetNumComponents(); i++ {
decode = append(decode, 0.0, 1.0)
}
return decode
}

func newPdfColorspaceDeviceNFromPdfObject(obj PdfObject) (*PdfColorspaceDeviceN, error) {
cs := NewPdfColorspaceDeviceN()

Expand Down

0 comments on commit 144fa3f

Please sign in to comment.