Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
G
golang
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
golang
Commits
a479bc8d
Commit
a479bc8d
authored
May 30, 2011
by
Nigel Tao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
image/png: fix encoding of images that don't start at (0, 0).
R=r CC=golang-dev
https://golang.org/cl/4560049
parent
5d5d84f3
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
84 additions
and
51 deletions
+84
-51
writer.go
src/pkg/image/png/writer.go
+40
-34
writer_test.go
src/pkg/image/png/writer_test.go
+44
-17
No files found.
src/pkg/image/png/writer.go
View file @
a479bc8d
...
@@ -174,7 +174,7 @@ func (e *encoder) Write(b []byte) (int, os.Error) {
...
@@ -174,7 +174,7 @@ func (e *encoder) Write(b []byte) (int, os.Error) {
// Chooses the filter to use for encoding the current row, and applies it.
// Chooses the filter to use for encoding the current row, and applies it.
// The return value is the index of the filter and also of the row in cr that has had it applied.
// The return value is the index of the filter and also of the row in cr that has had it applied.
func
filter
(
cr
[
][]
byte
,
pr
[]
byte
,
bpp
int
)
int
{
func
filter
(
cr
*
[
nFilter
][]
byte
,
pr
[]
byte
,
bpp
int
)
int
{
// We try all five filter types, and pick the one that minimizes the sum of absolute differences.
// We try all five filter types, and pick the one that minimizes the sum of absolute differences.
// This is the same heuristic that libpng uses, although the filters are attempted in order of
// This is the same heuristic that libpng uses, although the filters are attempted in order of
// estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
// estimated most likely to be minimal (ftUp, ftPaeth, ftNone, ftSub, ftAverage), rather than
...
@@ -304,7 +304,7 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
...
@@ -304,7 +304,7 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
// The +1 is for the per-row filter type, which is at cr[*][0].
// The +1 is for the per-row filter type, which is at cr[*][0].
b
:=
m
.
Bounds
()
b
:=
m
.
Bounds
()
var
cr
[
nFilter
][]
uint8
var
cr
[
nFilter
][]
uint8
for
i
:=
0
;
i
<
len
(
cr
);
i
++
{
for
i
:=
range
cr
{
cr
[
i
]
=
make
([]
uint8
,
1
+
bpp
*
b
.
Dx
())
cr
[
i
]
=
make
([]
uint8
,
1
+
bpp
*
b
.
Dx
())
cr
[
i
][
0
]
=
uint8
(
i
)
cr
[
i
][
0
]
=
uint8
(
i
)
}
}
...
@@ -312,78 +312,84 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
...
@@ -312,78 +312,84 @@ func writeImage(w io.Writer, m image.Image, cb int) os.Error {
for
y
:=
b
.
Min
.
Y
;
y
<
b
.
Max
.
Y
;
y
++
{
for
y
:=
b
.
Min
.
Y
;
y
<
b
.
Max
.
Y
;
y
++
{
// Convert from colors to bytes.
// Convert from colors to bytes.
i
:=
1
switch
cb
{
switch
cb
{
case
cbG8
:
case
cbG8
:
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
c
:=
image
.
GrayColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
GrayColor
)
c
:=
image
.
GrayColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
GrayColor
)
cr
[
0
][
x
+
1
]
=
c
.
Y
cr
[
0
][
i
]
=
c
.
Y
i
++
}
}
case
cbTC8
:
case
cbTC8
:
// We have previously verified that the alpha value is fully opaque.
// We have previously verified that the alpha value is fully opaque.
cr0
:=
cr
[
0
]
cr0
:=
cr
[
0
]
if
rgba
!=
nil
{
if
rgba
!=
nil
{
yoff
:=
y
*
rgba
.
Stride
yoff
:=
y
*
rgba
.
Stride
xoff
:=
3
*
b
.
Min
.
X
+
1
for
_
,
color
:=
range
rgba
.
Pix
[
yoff
+
b
.
Min
.
X
:
yoff
+
b
.
Max
.
X
]
{
for
_
,
color
:=
range
rgba
.
Pix
[
yoff
+
b
.
Min
.
X
:
yoff
+
b
.
Max
.
X
]
{
cr0
[
xoff
]
=
color
.
R
cr0
[
i
+
0
]
=
color
.
R
cr0
[
xoff
+
1
]
=
color
.
G
cr0
[
i
+
1
]
=
color
.
G
cr0
[
xoff
+
2
]
=
color
.
B
cr0
[
i
+
2
]
=
color
.
B
xoff
+=
3
i
+=
3
}
}
}
else
{
}
else
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
r
,
g
,
b
,
_
:=
m
.
At
(
x
,
y
)
.
RGBA
()
r
,
g
,
b
,
_
:=
m
.
At
(
x
,
y
)
.
RGBA
()
cr0
[
3
*
x
+
1
]
=
uint8
(
r
>>
8
)
cr0
[
i
+
0
]
=
uint8
(
r
>>
8
)
cr0
[
3
*
x
+
2
]
=
uint8
(
g
>>
8
)
cr0
[
i
+
1
]
=
uint8
(
g
>>
8
)
cr0
[
3
*
x
+
3
]
=
uint8
(
b
>>
8
)
cr0
[
i
+
2
]
=
uint8
(
b
>>
8
)
i
+=
3
}
}
}
}
case
cbP8
:
case
cbP8
:
rowOffset
:=
y
*
paletted
.
Stride
rowOffset
:=
y
*
paletted
.
Stride
copy
(
cr
[
0
][
b
.
Min
.
X
+
1
:
],
paletted
.
Pix
[
rowOffset
+
b
.
Min
.
X
:
rowOffset
+
b
.
Max
.
X
])
copy
(
cr
[
0
][
1
:
],
paletted
.
Pix
[
rowOffset
+
b
.
Min
.
X
:
rowOffset
+
b
.
Max
.
X
])
case
cbTCA8
:
case
cbTCA8
:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
c
:=
image
.
NRGBAColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
NRGBAColor
)
c
:=
image
.
NRGBAColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
NRGBAColor
)
cr
[
0
][
4
*
x
+
1
]
=
c
.
R
cr
[
0
][
i
+
0
]
=
c
.
R
cr
[
0
][
4
*
x
+
2
]
=
c
.
G
cr
[
0
][
i
+
1
]
=
c
.
G
cr
[
0
][
4
*
x
+
3
]
=
c
.
B
cr
[
0
][
i
+
2
]
=
c
.
B
cr
[
0
][
4
*
x
+
4
]
=
c
.
A
cr
[
0
][
i
+
3
]
=
c
.
A
i
+=
4
}
}
case
cbG16
:
case
cbG16
:
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
c
:=
image
.
Gray16ColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
Gray16Color
)
c
:=
image
.
Gray16ColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
Gray16Color
)
cr
[
0
][
2
*
x
+
1
]
=
uint8
(
c
.
Y
>>
8
)
cr
[
0
][
i
+
0
]
=
uint8
(
c
.
Y
>>
8
)
cr
[
0
][
2
*
x
+
2
]
=
uint8
(
c
.
Y
)
cr
[
0
][
i
+
1
]
=
uint8
(
c
.
Y
)
i
+=
2
}
}
case
cbTC16
:
case
cbTC16
:
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
// We have previously verified that the alpha value is fully opaque.
// We have previously verified that the alpha value is fully opaque.
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
r
,
g
,
b
,
_
:=
m
.
At
(
x
,
y
)
.
RGBA
()
r
,
g
,
b
,
_
:=
m
.
At
(
x
,
y
)
.
RGBA
()
cr
[
0
][
6
*
x
+
1
]
=
uint8
(
r
>>
8
)
cr
[
0
][
i
+
0
]
=
uint8
(
r
>>
8
)
cr
[
0
][
6
*
x
+
2
]
=
uint8
(
r
)
cr
[
0
][
i
+
1
]
=
uint8
(
r
)
cr
[
0
][
6
*
x
+
3
]
=
uint8
(
g
>>
8
)
cr
[
0
][
i
+
2
]
=
uint8
(
g
>>
8
)
cr
[
0
][
6
*
x
+
4
]
=
uint8
(
g
)
cr
[
0
][
i
+
3
]
=
uint8
(
g
)
cr
[
0
][
6
*
x
+
5
]
=
uint8
(
b
>>
8
)
cr
[
0
][
i
+
4
]
=
uint8
(
b
>>
8
)
cr
[
0
][
6
*
x
+
6
]
=
uint8
(
b
)
cr
[
0
][
i
+
5
]
=
uint8
(
b
)
i
+=
6
}
}
case
cbTCA16
:
case
cbTCA16
:
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
// Convert from image.Image (which is alpha-premultiplied) to PNG's non-alpha-premultiplied.
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
for
x
:=
b
.
Min
.
X
;
x
<
b
.
Max
.
X
;
x
++
{
c
:=
image
.
NRGBA64ColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
NRGBA64Color
)
c
:=
image
.
NRGBA64ColorModel
.
Convert
(
m
.
At
(
x
,
y
))
.
(
image
.
NRGBA64Color
)
cr
[
0
][
8
*
x
+
1
]
=
uint8
(
c
.
R
>>
8
)
cr
[
0
][
i
+
0
]
=
uint8
(
c
.
R
>>
8
)
cr
[
0
][
8
*
x
+
2
]
=
uint8
(
c
.
R
)
cr
[
0
][
i
+
1
]
=
uint8
(
c
.
R
)
cr
[
0
][
8
*
x
+
3
]
=
uint8
(
c
.
G
>>
8
)
cr
[
0
][
i
+
2
]
=
uint8
(
c
.
G
>>
8
)
cr
[
0
][
8
*
x
+
4
]
=
uint8
(
c
.
G
)
cr
[
0
][
i
+
3
]
=
uint8
(
c
.
G
)
cr
[
0
][
8
*
x
+
5
]
=
uint8
(
c
.
B
>>
8
)
cr
[
0
][
i
+
4
]
=
uint8
(
c
.
B
>>
8
)
cr
[
0
][
8
*
x
+
6
]
=
uint8
(
c
.
B
)
cr
[
0
][
i
+
5
]
=
uint8
(
c
.
B
)
cr
[
0
][
8
*
x
+
7
]
=
uint8
(
c
.
A
>>
8
)
cr
[
0
][
i
+
6
]
=
uint8
(
c
.
A
>>
8
)
cr
[
0
][
8
*
x
+
8
]
=
uint8
(
c
.
A
)
cr
[
0
][
i
+
7
]
=
uint8
(
c
.
A
)
i
+=
8
}
}
}
}
// Apply the filter.
// Apply the filter.
f
:=
filter
(
cr
[
0
:
nFilter
]
,
pr
,
bpp
)
f
:=
filter
(
&
cr
,
pr
,
bpp
)
// Write the compressed bytes.
// Write the compressed bytes.
_
,
err
=
zw
.
Write
(
cr
[
f
])
_
,
err
=
zw
.
Write
(
cr
[
f
])
...
...
src/pkg/image/png/writer_test.go
View file @
a479bc8d
...
@@ -5,9 +5,9 @@
...
@@ -5,9 +5,9 @@
package
png
package
png
import
(
import
(
"bytes"
"fmt"
"fmt"
"image"
"image"
"io"
"io/ioutil"
"io/ioutil"
"os"
"os"
"testing"
"testing"
...
@@ -15,21 +15,38 @@ import (
...
@@ -15,21 +15,38 @@ import (
func
diff
(
m0
,
m1
image
.
Image
)
os
.
Error
{
func
diff
(
m0
,
m1
image
.
Image
)
os
.
Error
{
b0
,
b1
:=
m0
.
Bounds
(),
m1
.
Bounds
()
b0
,
b1
:=
m0
.
Bounds
(),
m1
.
Bounds
()
if
!
b0
.
Eq
(
b1
)
{
if
!
b0
.
Size
()
.
Eq
(
b1
.
Size
()
)
{
return
fmt
.
Errorf
(
"dimensions differ: %v vs %v"
,
b0
,
b1
)
return
fmt
.
Errorf
(
"dimensions differ: %v vs %v"
,
b0
,
b1
)
}
}
dx
:=
b1
.
Min
.
X
-
b0
.
Min
.
X
dy
:=
b1
.
Min
.
Y
-
b0
.
Min
.
Y
for
y
:=
b0
.
Min
.
Y
;
y
<
b0
.
Max
.
Y
;
y
++
{
for
y
:=
b0
.
Min
.
Y
;
y
<
b0
.
Max
.
Y
;
y
++
{
for
x
:=
b0
.
Min
.
X
;
x
<
b0
.
Max
.
X
;
x
++
{
for
x
:=
b0
.
Min
.
X
;
x
<
b0
.
Max
.
X
;
x
++
{
r0
,
g0
,
b0
,
a0
:=
m0
.
At
(
x
,
y
)
.
RGBA
()
c0
:=
m0
.
At
(
x
,
y
)
r1
,
g1
,
b1
,
a1
:=
m1
.
At
(
x
,
y
)
.
RGBA
()
c1
:=
m1
.
At
(
x
+
dx
,
y
+
dy
)
r0
,
g0
,
b0
,
a0
:=
c0
.
RGBA
()
r1
,
g1
,
b1
,
a1
:=
c1
.
RGBA
()
if
r0
!=
r1
||
g0
!=
g1
||
b0
!=
b1
||
a0
!=
a1
{
if
r0
!=
r1
||
g0
!=
g1
||
b0
!=
b1
||
a0
!=
a1
{
return
fmt
.
Errorf
(
"colors differ at (%d, %d): %v vs %v"
,
x
,
y
,
m0
.
At
(
x
,
y
),
m1
.
At
(
x
,
y
)
)
return
fmt
.
Errorf
(
"colors differ at (%d, %d): %v vs %v"
,
x
,
y
,
c0
,
c1
)
}
}
}
}
}
}
return
nil
return
nil
}
}
func
encodeDecode
(
m
image
.
Image
)
(
image
.
Image
,
os
.
Error
)
{
b
:=
bytes
.
NewBuffer
(
nil
)
err
:=
Encode
(
b
,
m
)
if
err
!=
nil
{
return
nil
,
err
}
m
,
err
=
Decode
(
b
)
if
err
!=
nil
{
return
nil
,
err
}
return
m
,
nil
}
func
TestWriter
(
t
*
testing
.
T
)
{
func
TestWriter
(
t
*
testing
.
T
)
{
// The filenames variable is declared in reader_test.go.
// The filenames variable is declared in reader_test.go.
names
:=
filenames
names
:=
filenames
...
@@ -44,27 +61,17 @@ func TestWriter(t *testing.T) {
...
@@ -44,27 +61,17 @@ func TestWriter(t *testing.T) {
t
.
Error
(
fn
,
err
)
t
.
Error
(
fn
,
err
)
continue
continue
}
}
// Read the image again, and push it through a pipe that encodes at the write end, and decodes at the read end.
// Read the image again, encode it, and decode it.
pr
,
pw
:=
io
.
Pipe
()
defer
pr
.
Close
()
go
func
()
{
defer
pw
.
Close
()
m1
,
err
:=
readPng
(
qfn
)
m1
,
err
:=
readPng
(
qfn
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Error
(
fn
,
err
)
t
.
Error
(
fn
,
err
)
return
return
}
}
err
=
Encode
(
pw
,
m1
)
m2
,
err
:=
encodeDecode
(
m1
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Error
(
fn
,
err
)
t
.
Error
(
fn
,
err
)
return
return
}
}
}()
m2
,
err
:=
Decode
(
pr
)
if
err
!=
nil
{
t
.
Error
(
fn
,
err
)
continue
}
// Compare the two.
// Compare the two.
err
=
diff
(
m0
,
m2
)
err
=
diff
(
m0
,
m2
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -74,6 +81,26 @@ func TestWriter(t *testing.T) {
...
@@ -74,6 +81,26 @@ func TestWriter(t *testing.T) {
}
}
}
}
func
TestSubimage
(
t
*
testing
.
T
)
{
m0
:=
image
.
NewRGBA
(
256
,
256
)
for
y
:=
0
;
y
<
256
;
y
++
{
for
x
:=
0
;
x
<
256
;
x
++
{
m0
.
Set
(
x
,
y
,
image
.
RGBAColor
{
uint8
(
x
),
uint8
(
y
),
0
,
255
})
}
}
m0
.
Rect
=
image
.
Rect
(
50
,
30
,
250
,
130
)
m1
,
err
:=
encodeDecode
(
m0
)
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
err
=
diff
(
m0
,
m1
)
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
}
func
BenchmarkEncodePaletted
(
b
*
testing
.
B
)
{
func
BenchmarkEncodePaletted
(
b
*
testing
.
B
)
{
b
.
StopTimer
()
b
.
StopTimer
()
img
:=
image
.
NewPaletted
(
640
,
480
,
img
:=
image
.
NewPaletted
(
640
,
480
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment