diff --git a/ebitenutil/text.png b/ebitenutil/text.png index 9d94ec7d6..e4ebdbf8a 100644 Binary files a/ebitenutil/text.png and b/ebitenutil/text.png differ diff --git a/internal/png/stdlibreader.go b/internal/png/stdlibreader.go index adbe2bb51..6f8917735 100644 --- a/internal/png/stdlibreader.go +++ b/internal/png/stdlibreader.go @@ -53,6 +53,10 @@ func cbPaletted(cb int) bool { return cbP1 <= cb && cb <= cbP8 } +func cbTrueColor(cb int) bool { + return cb == cbTC8 || cb == cbTC16 +} + // Filter type, as per the PNG spec. const ( ftNone = 0 @@ -872,7 +876,7 @@ func (d *decoder) parseIEND(length uint32) error { return d.verifyChecksum() } -func (d *decoder) parseChunk() error { +func (d *decoder) parseChunk(configOnly bool) error { // Read the length and chunk type. if _, err := io.ReadFull(d.r, d.tmp[:8]); err != nil { return err @@ -900,6 +904,10 @@ func (d *decoder) parseChunk() error { if d.stage != dsSeenPLTE { return chunkOrderError } + } else if cbTrueColor(d.cb) { + if d.stage != dsSeenIHDR && d.stage != dsSeenPLTE { + return chunkOrderError + } } else if d.stage != dsSeenIHDR { return chunkOrderError } @@ -917,6 +925,9 @@ func (d *decoder) parseChunk() error { break } d.stage = dsSeenIDAT + if configOnly { + return nil + } return d.parseIDAT(length) case "IEND": if d.stage != dsSeenIDAT { @@ -976,7 +987,7 @@ func Decode(r io.Reader) (image.Image, error) { return nil, err } for d.stage != dsSeenIEND { - if err := d.parseChunk(); err != nil { + if err := d.parseChunk(false); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } @@ -999,21 +1010,26 @@ func DecodeConfig(r io.Reader) (image.Config, error) { } return image.Config{}, err } + for { - if err := d.parseChunk(); err != nil { + if err := d.parseChunk(true); err != nil { if err == io.EOF { err = io.ErrUnexpectedEOF } return image.Config{}, err } - paletted := cbPaletted(d.cb) - if d.stage == dsSeenIHDR && !paletted { - break - } - if d.stage == dsSeenPLTE && paletted { - break + + if cbPaletted(d.cb) { + if d.stage >= dsSeentRNS { + break + } + } else { + if d.stage >= dsSeenIHDR { + break + } } } + var cm color.Model switch d.cb { case cbG1, cbG2, cbG4, cbG8: diff --git a/internal/png/stdlibwriter.go b/internal/png/stdlibwriter.go index a05054a96..95cd0d384 100644 --- a/internal/png/stdlibwriter.go +++ b/internal/png/stdlibwriter.go @@ -452,6 +452,36 @@ func (e *encoder) writeImage(w io.Writer, m image.Image, cb int, level int) erro if nrgba != nil { offset := (y - b.Min.Y) * nrgba.Stride copy(cr[0][1:], nrgba.Pix[offset:offset+b.Dx()*4]) + } else if rgba != nil { + dst := cr[0][1:] + src := rgba.Pix[rgba.PixOffset(b.Min.X, y):rgba.PixOffset(b.Max.X, y)] + for ; len(src) >= 4; dst, src = dst[4:], src[4:] { + d := (*[4]byte)(dst) + s := (*[4]byte)(src) + if s[3] == 0x00 { + d[0] = 0 + d[1] = 0 + d[2] = 0 + d[3] = 0 + } else if s[3] == 0xff { + copy(d[:], s[:]) + } else { + // This code does the same as color.NRGBAModel.Convert( + // rgba.At(x, y)).(color.NRGBA) but with no extra memory + // allocations or interface/function call overhead. + // + // The multiplier m combines 0x101 (which converts + // 8-bit color to 16-bit color) and 0xffff (which, when + // combined with the division-by-a, converts from + // alpha-premultiplied to non-alpha-premultiplied). + const m = 0x101 * 0xffff + a := uint32(s[3]) * 0x101 + d[0] = uint8((uint32(s[0]) * m / a) >> 8) + d[1] = uint8((uint32(s[1]) * m / a) >> 8) + d[2] = uint8((uint32(s[2]) * m / a) >> 8) + d[3] = s[3] + } + } } else { // Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied. for x := b.Min.X; x < b.Max.X; x++ { diff --git a/keys.go b/keys.go index 02e992445..e41523122 100644 --- a/keys.go +++ b/keys.go @@ -24,7 +24,7 @@ import ( ) // A Key represents a keyboard key. -// These keys represent pysical keys of US keyboard. +// These keys represent physical keys of US keyboard. // For example, KeyQ represents Q key on US keyboards and ' (quote) key on Dvorak keyboards. type Key int