mirror of
https://github.com/status-im/nimPNG.git
synced 2025-02-12 04:26:24 +00:00
apng minor adjusment
This commit is contained in:
parent
6853f199ef
commit
05cce9b58f
152
nimPNG.nim
152
nimPNG.nim
@ -181,6 +181,8 @@ type
|
|||||||
blendOp*: APNG_BLEND_OP
|
blendOp*: APNG_BLEND_OP
|
||||||
|
|
||||||
APNGFrameData = ref object of APNGFrameChunk
|
APNGFrameData = ref object of APNGFrameChunk
|
||||||
|
# during decoding frameDataPos points to chunk.data[pos]
|
||||||
|
# during encoding frameDataPos points to png.apngPixels[pos] and png.apngChunks[pos]
|
||||||
frameDataPos: int
|
frameDataPos: int
|
||||||
|
|
||||||
PNGPass = object
|
PNGPass = object
|
||||||
@ -214,9 +216,13 @@ type
|
|||||||
second*: int #range[0..60] #to allow for leap seconds
|
second*: int #range[0..60] #to allow for leap seconds
|
||||||
|
|
||||||
PNG* = ref object
|
PNG* = ref object
|
||||||
|
# during encoding, settings is PNGEncoder
|
||||||
|
# during decoding, settings is PNGDecoder
|
||||||
settings*: PNGSettings
|
settings*: PNGSettings
|
||||||
chunks*: seq[PNGChunk]
|
chunks*: seq[PNGChunk]
|
||||||
pixels*: string
|
pixels*: string
|
||||||
|
# during encoding, apngChunks contains only fcTL chunks
|
||||||
|
# during decoding, apngChunks contains both fcTL and fdAT chunks
|
||||||
apngChunks*: seq[APNGFrameChunk]
|
apngChunks*: seq[APNGFrameChunk]
|
||||||
firstFrameIsDefaultImage*: bool
|
firstFrameIsDefaultImage*: bool
|
||||||
isAPNG*: bool
|
isAPNG*: bool
|
||||||
@ -1912,90 +1918,108 @@ proc toString(chunk: APNGFrameData): string =
|
|||||||
result = newString(fdatLen)
|
result = newString(fdatLen)
|
||||||
copyMem(result[0].addr, fdatAddr, fdatLen)
|
copyMem(result[0].addr, fdatAddr, fdatLen)
|
||||||
|
|
||||||
|
type
|
||||||
|
APNG = ref object
|
||||||
|
png: PNG
|
||||||
|
result: PNGResult
|
||||||
|
|
||||||
|
proc processingAPNG(apng: APNG, colorType: PNGcolorType, bitDepth: int) =
|
||||||
|
let header = PNGHeader(apng.png.getChunk(IHDR))
|
||||||
|
var
|
||||||
|
actl = APNGAnimationControl(apng.png.getChunk(acTL))
|
||||||
|
frameControl = newSeqOfCap[APNGFrameControl](actl.numFrames)
|
||||||
|
frameData = newSeqOfCap[string](actl.numFrames)
|
||||||
|
numFrames = 0
|
||||||
|
lastChunkType = PNGChunkType(0)
|
||||||
|
start = 0
|
||||||
|
|
||||||
|
if apng.png.firstFrameIsDefaultImage:
|
||||||
|
start = 1
|
||||||
|
# IDAT already processed, so we add a dummy here
|
||||||
|
frameData.add string(nil)
|
||||||
|
|
||||||
|
for x in apng.png.apngChunks:
|
||||||
|
if x.chunkType == fcTL:
|
||||||
|
frameControl.add APNGFrameControl(x)
|
||||||
|
inc numFrames
|
||||||
|
lastChunkType = fcTL
|
||||||
|
else:
|
||||||
|
let y = APNGFrameData(x)
|
||||||
|
if lastChunkType == fdAT:
|
||||||
|
frameData[^1].add y.toString()
|
||||||
|
else:
|
||||||
|
frameData.add y.toString()
|
||||||
|
lastChunkType = fdAT
|
||||||
|
|
||||||
|
if actl.numFrames == 0 or actl.numFrames != numFrames or actl.numFrames != frameData.len:
|
||||||
|
raise PNGError("animation numFrames error")
|
||||||
|
|
||||||
|
apng.png.apngPixels = newSeqOfCap[string](numFrames)
|
||||||
|
|
||||||
|
if apng.result != nil:
|
||||||
|
apng.result.frames = newSeqOfCap[APNGFrame](numFrames)
|
||||||
|
|
||||||
|
if apng.png.firstFrameIsDefaultImage:
|
||||||
|
let ctl = frameControl[0]
|
||||||
|
if ctl.width != header.width or ctl.height != header.height:
|
||||||
|
raise PNGError("animation control error: dimension")
|
||||||
|
if ctl.xOffset != 0 or ctl.xOffset != 0:
|
||||||
|
raise PNGError("animation control error: offset")
|
||||||
|
|
||||||
|
if apng.result != nil:
|
||||||
|
var frame = new(APNGFrame)
|
||||||
|
frame.ctl = ctl
|
||||||
|
frame.data = apng.result.data
|
||||||
|
apng.result.frames.add frame
|
||||||
|
|
||||||
|
for i in start..<numFrames:
|
||||||
|
let ctl = frameControl[i]
|
||||||
|
var nz = nzInflateInit(frameData[i])
|
||||||
|
nz.ignoreAdler32 = PNGDecoder(apng.png.settings).ignoreAdler32
|
||||||
|
let idat = zlib_decompress(nz)
|
||||||
|
apng.png.postProcessScanLines(ctl, idat)
|
||||||
|
|
||||||
|
if apng.result != nil:
|
||||||
|
if PNGDecoder(apng.png.settings).colorConvert:
|
||||||
|
apng.result.frames.add apng.png.convert(colorType, bitDepth, ctl, apng.png.apngPixels[^1])
|
||||||
|
else:
|
||||||
|
var frame = new(APNGFrame)
|
||||||
|
frame.ctl = ctl
|
||||||
|
frame.data = apng.png.apngPixels[^1]
|
||||||
|
apng.result.frames.add frame
|
||||||
|
|
||||||
proc decodePNG*(s: Stream, colorType: PNGcolorType, bitDepth: int, settings = PNGDecoder(nil)): PNGResult =
|
proc decodePNG*(s: Stream, colorType: PNGcolorType, bitDepth: int, settings = PNGDecoder(nil)): PNGResult =
|
||||||
if not bitDepthAllowed(colorType, bitDepth):
|
if not bitDepthAllowed(colorType, bitDepth):
|
||||||
raise PNGError("colorType and bitDepth combination not allowed")
|
raise PNGError("colorType and bitDepth combination not allowed")
|
||||||
|
|
||||||
var png = s.parsePNG(settings)
|
var png = s.parsePNG(settings)
|
||||||
let header = PNGHeader(png.getChunk(IHDR))
|
|
||||||
# shadowing the settings
|
|
||||||
let settings = PNGDecoder(png.settings)
|
|
||||||
png.postProcessScanLines()
|
png.postProcessScanLines()
|
||||||
|
|
||||||
if settings.colorConvert:
|
if PNGDecoder(png.settings).colorConvert:
|
||||||
result = png.convert(colorType, bitDepth)
|
result = png.convert(colorType, bitDepth)
|
||||||
else:
|
else:
|
||||||
new(result)
|
new(result)
|
||||||
|
let header = PNGHeader(png.getChunk(IHDR))
|
||||||
result.width = header.width
|
result.width = header.width
|
||||||
result.height = header.height
|
result.height = header.height
|
||||||
result.data = png.pixels
|
result.data = png.pixels
|
||||||
|
|
||||||
if png.isAPNG:
|
if png.isAPNG:
|
||||||
var
|
var apng = APNG(png: png, result: result)
|
||||||
actl = APNGAnimationControl(png.getChunk(acTL))
|
apng.processingAPNG(colorType, bitDepth)
|
||||||
frameControl = newSeqOfCap[APNGFrameControl](actl.numFrames)
|
|
||||||
frameData = newSeqOfCap[string](actl.numFrames)
|
|
||||||
numFrames = 0
|
|
||||||
lastChunkType = PNGChunkType(0)
|
|
||||||
start = 0
|
|
||||||
|
|
||||||
if png.firstFrameIsDefaultImage:
|
|
||||||
start = 1
|
|
||||||
# IDAT already processed, so we add a dummy here
|
|
||||||
frameData.add string(nil)
|
|
||||||
|
|
||||||
for x in png.apngChunks:
|
|
||||||
if x.chunkType == fcTL:
|
|
||||||
frameControl.add APNGFrameControl(x)
|
|
||||||
inc numFrames
|
|
||||||
lastChunkType = fcTL
|
|
||||||
else:
|
|
||||||
let y = APNGFrameData(x)
|
|
||||||
if lastChunkType == fdAT:
|
|
||||||
frameData[^1].add y.toString()
|
|
||||||
else:
|
|
||||||
frameData.add y.toString()
|
|
||||||
lastChunkType = fdAT
|
|
||||||
|
|
||||||
if actl.numFrames == 0 or actl.numFrames != numFrames or actl.numFrames != frameData.len:
|
|
||||||
raise PNGError("animation numFrames error")
|
|
||||||
|
|
||||||
png.apngPixels = newSeqOfCap[string](numFrames)
|
|
||||||
result.frames = newSeqOfCap[APNGFrame](numFrames)
|
|
||||||
|
|
||||||
if png.firstFrameIsDefaultImage:
|
|
||||||
let ctl = frameControl[0]
|
|
||||||
if ctl.width != header.width or ctl.height != header.height:
|
|
||||||
raise PNGError("animation control error: dimension")
|
|
||||||
if ctl.xOffset != 0 or ctl.xOffset != 0:
|
|
||||||
raise PNGError("animation control error: offset")
|
|
||||||
var frame = new(APNGFrame)
|
|
||||||
frame.ctl = ctl
|
|
||||||
frame.data = result.data
|
|
||||||
result.frames.add frame
|
|
||||||
|
|
||||||
for i in start..<numFrames:
|
|
||||||
let ctl = frameControl[i]
|
|
||||||
var nz = nzInflateInit(frameData[i])
|
|
||||||
nz.ignoreAdler32 = PNGDecoder(png.settings).ignoreAdler32
|
|
||||||
let idat = zlib_decompress(nz)
|
|
||||||
png.postProcessScanLines(ctl, idat)
|
|
||||||
|
|
||||||
if PNGDecoder(png.settings).colorConvert:
|
|
||||||
result.frames.add png.convert(colorType, bitDepth, ctl, png.apngPixels[^1])
|
|
||||||
else:
|
|
||||||
var frame = new(APNGFrame)
|
|
||||||
frame.ctl = ctl
|
|
||||||
frame.data = png.apngPixels[^1]
|
|
||||||
result.frames.add frame
|
|
||||||
|
|
||||||
proc decodePNG*(s: Stream, settings = PNGDecoder(nil)): PNG =
|
proc decodePNG*(s: Stream, settings = PNGDecoder(nil)): PNG =
|
||||||
var png = s.parsePNG(settings)
|
var png = s.parsePNG(settings)
|
||||||
png.postProcessScanLines()
|
png.postProcessScanLines()
|
||||||
|
|
||||||
|
if png.isAPNG:
|
||||||
|
var apng = APNG(png: png, result: nil)
|
||||||
|
apng.processingAPNG(PNGColorType(0), 0)
|
||||||
|
|
||||||
result = png
|
result = png
|
||||||
|
|
||||||
when not defined(js):
|
when not defined(js):
|
||||||
proc loadPNG*(fileName: string, colorType: PNGcolorType, bitDepth: int, settings: PNGDecoder): PNGResult =
|
proc loadPNG*(fileName: string, colorType: PNGColorType, bitDepth: int, settings: PNGDecoder): PNGResult =
|
||||||
try:
|
try:
|
||||||
var s = newFileStream(fileName, fmRead)
|
var s = newFileStream(fileName, fmRead)
|
||||||
if s == nil: return nil
|
if s == nil: return nil
|
||||||
@ -2389,6 +2413,10 @@ method writeChunk(chunk: PNGICCProfile, png: PNG): bool =
|
|||||||
chunk.writeString zlib_compress(nz)
|
chunk.writeString zlib_compress(nz)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
|
#method writeChunk(chunk: PNGICCProfile, png: PNG): bool =
|
||||||
|
#method writeChunk(chunk: PNGICCProfile, png: PNG): bool =
|
||||||
|
#method writeChunk(chunk: PNGICCProfile, png: PNG): bool =
|
||||||
|
|
||||||
proc isGreyscaleType(mode: PNGColorMode): bool =
|
proc isGreyscaleType(mode: PNGColorMode): bool =
|
||||||
result = mode.colorType in {LCT_GREY, LCT_GREY_ALPHA}
|
result = mode.colorType in {LCT_GREY, LCT_GREY_ALPHA}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ to create PNG:
|
|||||||
special notes:
|
special notes:
|
||||||
|
|
||||||
* Use **loadPNG** or **savePNG** if you need specific input/output format by supplying supported **colorType** and **bitDepth** information.
|
* Use **loadPNG** or **savePNG** if you need specific input/output format by supplying supported **colorType** and **bitDepth** information.
|
||||||
* Use **encodePNG** or **decodePNG** to do *in-memory* encoding/decoding by supplying desired colorType and bitDepth information
|
* Use **encodePNG** or **decodePNG** to do *in-memory* encoding/decoding by supplying desired **colorType** and **bitDepth** information
|
||||||
|
|
||||||
pixels are stored as raw bytes using Nim's string as container:
|
pixels are stored as raw bytes using Nim's string as container:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user