#FEATURE by cg
authorClaus Gittinger <cg@exept.de>
Mon, 18 Sep 2017 09:10:55 +0200
changeset 4017 0fea2b021ca6
parent 4016 47c4ea8134ec
child 4018 5cfaf6f42f16
#FEATURE by cg some support for grey+alpha
PNGReader.st
--- a/PNGReader.st	Mon Sep 18 09:10:16 2017 +0200
+++ b/PNGReader.st	Mon Sep 18 09:10:55 2017 +0200
@@ -17,7 +17,8 @@
 	instanceVariableNames:'colorType bitsPerChannel compressionMethod filterMethod
 		interlaceMode bytesPerScanline globalDataChunk thisScanline
 		prevScanline processTextChunks specialChunkHandlers
-		paletteAlphaEntries paletteIndexForMaskedPixels image'
+		paletteAlphaEntries paletteIndexForMaskedPixels image forceRGB
+		depthImage'
 	classVariableNames:'ColorTypeGray ColorTypeGrayAlpha ColorTypePalette ColorTypeRGB
 		ColorTypeRGBAlpha Verbose'
 	poolDictionaries:''
@@ -194,7 +195,7 @@
      Leave the image description in instance variables
      (i.e. to get the image, ask with image)."
 
-    |header|
+    |header bitsPerSampleOut bytesPerScanlineOut|
 
     inStream := aStream.
     aStream binary.
@@ -238,9 +239,39 @@
 
     [self getChunk] whileTrue.
 
+    "/ kludge: if we get a grey image with alpha,
+    "/ load as rgb+alpha
+    forceRGB := false. 
+    depthImage := depth.
+    
+    "/ bytesPerScanline is always the in-bytesPerScanline
     bytesPerScanline := self bytesPerRow.
-    data := ByteArray new:(bytesPerScanline * height).
-
+    bytesPerScanlineOut := bytesPerScanline.
+    
+    samplesPerPixel == 2 ifTrue:[
+        photometric == #blackIs0 ifTrue:[
+            depth == 16 ifTrue:[
+                "/ force to r8+g8+b8+a8
+                forceRGB := true. 
+                depthImage := 32.
+                bytesPerScanlineOut := width * 4.
+                bitsPerSampleOut := #[8 8 8 8].
+            ] ifFalse:[
+                depth == 32 ifTrue:[
+                    "/ force to r8+g8+b8+a8
+                    forceRGB := true. 
+                    depthImage := 32.
+                    bytesPerScanlineOut := width * 4.
+                    bitsPerSampleOut := #[8 8 8 8].
+                ] ifFalse:[
+                    self halt.
+                ].    
+            ].    
+        ].    
+    ].
+    
+    data := ByteArray new:(bytesPerScanlineOut * height).
+    
     globalDataChunk notNil ifTrue:[
         self processGlobalIDATChunk.
         globalDataChunk := nil.
@@ -260,6 +291,13 @@
         self generateMaskFromPaletteAlphaEntries
     ].
     
+    forceRGB ifTrue:[ 
+        depth := depthImage.
+        photometric := #rgba.
+        bitsPerSample := bitsPerSampleOut.
+        samplesPerPixel := 4.
+    ].    
+
     "
      PNGReader fromFile:'/home/cg/libpng-0.89c/pngtest.png'
 
@@ -268,7 +306,7 @@
      
     "
 
-    "Modified: / 22-02-2017 / 11:00:19 / cg"
+    "Modified: / 17-09-2017 / 15:30:45 / cg"
 ! !
 
 !PNGReader methodsFor:'reading-private'!
@@ -1359,6 +1397,169 @@
     self error:'unsupported depth'.
 !
 
+copyPixelsGrayAlpha:y at:startX by:incX 
+    "Handle interlaced pixels of supported colorTypes."
+
+    |srcIndex "{ Class: SmallInteger }"
+     srcMask  "{ Class: SmallInteger }"
+     nPixels  "{ Class: SmallInteger }"
+     dstIndex "{ Class: SmallInteger }"
+     dstMask  "{ Class: SmallInteger }"
+     x        "{ Class: SmallInteger }"
+     bits     "{ Class: SmallInteger }"
+     bitMask  "{ Class: SmallInteger }"
+     rS       "{ Class: SmallInteger }"
+     lS       "{ Class: SmallInteger }"
+     rowIndex "{ Class: SmallInteger }"
+     pix alpha gray|
+    
+    nPixels := width // incX.
+    srcIndex := 0. srcMask := 0. x := startX.
+
+"/    depth == 1 ifTrue:[
+"/        dstIndex := (y * bytesPerScanline) + (startX // 8) + 1.
+"/        dstMask := 16r80 >> (x \\ 8).
+"/
+"/        1 to:nPixels do:[:cnt |
+"/            srcMask == 0 ifTrue:[
+"/                srcMask := 16r80.
+"/                srcIndex := srcIndex + 1.
+"/                bits := thisScanline at:srcIndex. 
+"/            ].
+"/            bitMask := (bits bitAnd:srcMask) == 0 ifTrue:[0] ifFalse:[dstMask].
+"/            bitMask ~~ 0 ifTrue:[
+"/                data at:dstIndex put:((data at:dstIndex) bitOr:bitMask).
+"/            ].    
+"/            x := x + incX.
+"/            dstMask := dstMask >> incX.
+"/            dstMask == 0 ifTrue:[
+"/                dstIndex := dstIndex + 1.
+"/                dstMask := 16r80 >> (x \\ 8).
+"/            ].
+"/            srcMask := srcMask bitShift:-1.
+"/        ].
+"/        ^ self.
+"/    ].
+"/    
+"/    depth == 2 ifTrue:[
+"/        dstIndex := (y * bytesPerScanline) + (startX // 4) + 1.
+"/        lS := 6 - ((x \\ 4) * 2).
+"/        rS := -1. 
+"/        1 to:nPixels do:[:cnt |
+"/            rS < 0 ifTrue:[
+"/                srcIndex := srcIndex + 1.
+"/                bits := thisScanline at:srcIndex.
+"/                rS := 6.
+"/            ].
+"/            bitMask := ((bits >> rS) bitAnd:2r11).
+"/            bitMask ~~ 0 ifTrue:[
+"/                bitMask := bitMask << lS.
+"/                data at:dstIndex put:((data at:dstIndex) bitOr:bitMask).
+"/            ].    
+"/            lS := lS - (incX * 2).
+"/            lS < 0 ifTrue:[
+"/                lS <= -8 ifTrue:[
+"/                    dstIndex := dstIndex + 2.
+"/                    lS := lS + 8 + 8.
+"/                ] ifFalse:[
+"/                    dstIndex := dstIndex + 1.
+"/                    lS := lS + 8.
+"/                ].    
+"/            ].
+"/            rS := rS - 2.
+"/        ].
+"/        ^ self.
+"/    ].
+"/    
+"/    depth == 4 ifTrue:[
+"/        rowIndex := (y * bytesPerScanline) + 1.
+"/        dstIndex := rowIndex + (x // 2).
+"/        rS := -1. 
+"/        1 to:nPixels do:[:cnt |
+"/            rS < 0 ifTrue:[
+"/                srcIndex := srcIndex + 1.
+"/                bits := thisScanline at:srcIndex.
+"/                rS := 4.
+"/            ].
+"/            bitMask := ((bits >> rS) bitAnd:2r1111).
+"/            x even ifTrue:[    
+"/                bitMask := bitMask bitShift:4.
+"/            ].    
+"/            data at:dstIndex put:((data at:dstIndex) bitOr:bitMask).
+"/            x := x + incX.
+"/            dstIndex := rowIndex + (x // 2).
+"/            rS := rS - 4.
+"/        ].
+"/        ^ self.
+"/    ].
+"/    
+"/    depth == 8 ifTrue:[
+"/        srcIndex := 1.
+"/        dstIndex := (y * bytesPerScanline) + (startX) + 1.
+"/
+"/        1 to:nPixels do:[:n |
+"/            data at:dstIndex put:(thisScanline at:srcIndex).
+"/            srcIndex := srcIndex + 1.
+"/            dstIndex := dstIndex + incX
+"/        ].
+"/        ^ self.
+"/    ].
+
+    forceRGB ifTrue:[
+        (depth == 16 and:[depthImage == 32]) ifTrue:[
+            "/ converting gray8+alpha8 to r8+g8+b8+alpha8
+
+            srcIndex := 1.
+            dstIndex := (y * (width * 4)) + (startX*4) + 1.
+
+            1 to:nPixels do:[:n |
+                pix := thisScanline unsignedInt16At:srcIndex.
+                alpha := pix bitAnd:16rFF.
+                gray := (pix rightShift:8) bitAnd:16rFF.
+                "/ put r-g-b-a
+                data at:dstIndex put:gray.
+                data at:dstIndex+1 put:gray.
+                data at:dstIndex+2 put:gray.
+                data at:dstIndex+3 put:alpha.
+
+                srcIndex := srcIndex + 2.
+                dstIndex := dstIndex + (incX * 4)
+            ].
+            ^ self.
+        ].
+        (depth == 32 and:[depthImage == 32]) ifTrue:[
+            "/ converting gray16+alpha16 to r8+g8+b8+alpha8
+
+            srcIndex := 1.
+            dstIndex := (y * (width * 4)) + (startX*4) + 1.
+
+            1 to:nPixels do:[:n |
+                pix := thisScanline unsignedInt32At:srcIndex.
+                
+                alpha := pix bitAnd:16rFFFF.
+                gray := (pix rightShift:16) bitAnd:16rFFFF.
+                "/ reduce to 8 bit
+                alpha := alpha rightShift:8.
+                gray := gray rightShift:8.
+                
+                "/ put r-g-b-a
+                data at:dstIndex put:gray.
+                data at:dstIndex+1 put:gray.
+                data at:dstIndex+2 put:gray.
+                data at:dstIndex+3 put:alpha.
+
+                srcIndex := srcIndex + 4.
+                dstIndex := dstIndex + (incX * 4)
+            ].
+            ^ self.
+        ].
+    ].
+    self error:'unsupported depth'.
+
+    "Created: / 17-09-2017 / 14:20:24 / cg"
+    "Modified: / 17-09-2017 / 15:28:38 / cg"
+!
+
 copyPixelsIndexed:y at:startX by:incX 
     self copyPixelsGray:y at:startX by:incX 
 !