2012-03-24 01:36:37 +01:00
#encoding: utf8
from nose . tools import *
import os , sys
2012-10-16 16:30:31 +02:00
from utilities import execution_path , run_tests , Todo
2012-10-16 17:16:45 +02:00
from utilities import get_unique_colors , pixel2channels , side_by_side_image
2012-03-24 01:36:37 +01:00
import mapnik
def setup ( ) :
# All of the paths used are relative, if we run the tests
# from another directory we need to chdir()
os . chdir ( execution_path ( ' . ' ) )
2012-08-29 03:53:27 +02:00
def is_pre ( color , alpha ) :
return ( color * 255.0 / alpha ) < = 255
def debug_image ( image , step = 2 ) :
for x in range ( 0 , image . width ( ) , step ) :
for y in range ( 0 , image . height ( ) , step ) :
pixel = image . get_pixel ( x , y )
2012-10-05 01:39:00 +02:00
red , green , blue , alpha = pixel2channels ( pixel )
2012-08-29 03:53:27 +02:00
print " rgba( %s , %s , %s , %s ) at %s , %s " % ( red , green , blue , alpha , x , y )
2012-10-16 14:32:57 +02:00
def replace_style ( m , name , style ) :
m . remove_style ( name )
m . append_style ( name , style )
2012-08-29 03:53:27 +02:00
# note: it is impossible to know for all pixel colors
# we can only detect likely cases of non premultiplied colors
def validate_pixels_are_not_premultiplied ( image ) :
over_alpha = False
transparent = True
fully_opaque = True
2012-08-03 23:13:13 +02:00
for x in range ( 0 , image . width ( ) , 2 ) :
for y in range ( 0 , image . height ( ) , 2 ) :
pixel = image . get_pixel ( x , y )
2012-10-05 01:39:00 +02:00
red , green , blue , alpha = pixel2channels ( pixel )
2012-08-29 03:53:27 +02:00
if alpha > 0 :
transparent = False
if alpha < 255 :
fully_opaque = False
color_max = max ( red , green , blue )
if color_max > alpha :
over_alpha = True
return over_alpha or transparent or fully_opaque
def validate_pixels_are_not_premultiplied2 ( image ) :
looks_not_multiplied = False
for x in range ( 0 , image . width ( ) , 2 ) :
for y in range ( 0 , image . height ( ) , 2 ) :
pixel = image . get_pixel ( x , y )
2012-10-05 01:39:00 +02:00
red , green , blue , alpha = pixel2channels ( pixel )
2012-08-29 03:53:27 +02:00
#each value of the color channels will never be bigger than that of the alpha channel.
if alpha > 0 :
if red > 0 and red > alpha :
print ' red: %s , a: %s ' % ( red , alpha )
looks_not_multiplied = True
return looks_not_multiplied
2012-08-03 23:13:13 +02:00
def validate_pixels_are_premultiplied ( image ) :
bad_pixels = [ ]
for x in range ( 0 , image . width ( ) , 2 ) :
for y in range ( 0 , image . height ( ) , 2 ) :
pixel = image . get_pixel ( x , y )
2012-10-05 01:39:00 +02:00
red , green , blue , alpha = pixel2channels ( pixel )
2012-08-29 03:53:27 +02:00
if alpha > 0 :
pixel = image . get_pixel ( x , y )
is_valid = ( ( 0 < = red < = alpha ) and is_pre ( red , alpha ) ) \
and ( ( 0 < = green < = alpha ) and is_pre ( green , alpha ) ) \
and ( ( 0 < = blue < = alpha ) and is_pre ( blue , alpha ) ) \
and ( alpha > = 0 and alpha < = 255 )
if not is_valid :
bad_pixels . append ( " rgba( %s , %s , %s , %s ) at %s , %s " % ( red , green , blue , alpha , x , y ) )
2012-08-03 23:13:13 +02:00
num_bad = len ( bad_pixels )
return ( num_bad == 0 , bad_pixels )
2012-03-24 01:36:37 +01:00
def test_compare_images ( ) :
b = mapnik . Image . open ( ' ./images/support/b.png ' )
2012-06-21 21:44:57 +02:00
b . premultiply ( )
2012-10-02 03:51:53 +02:00
num_ops = len ( mapnik . CompositeOp . names )
successes = [ ]
fails = [ ]
2012-03-24 01:36:37 +01:00
for name in mapnik . CompositeOp . names :
a = mapnik . Image . open ( ' ./images/support/a.png ' )
2012-06-21 21:44:57 +02:00
a . premultiply ( )
2012-03-24 01:36:37 +01:00
a . composite ( b , getattr ( mapnik . CompositeOp , name ) )
actual = ' /tmp/mapnik-comp-op-test- ' + name + ' .png '
expected = ' images/composited/ ' + name + ' .png '
2012-08-03 23:13:13 +02:00
valid = validate_pixels_are_premultiplied ( a )
if not valid [ 0 ] :
2012-10-05 01:39:00 +02:00
fails . append ( ' %s not validly premultiplied!: \n \t %s pixels ( %s ) ' % ( name , len ( valid [ 1 ] ) , valid [ 1 ] [ 0 ] ) )
2012-06-21 21:44:57 +02:00
a . demultiply ( )
2012-08-29 03:53:27 +02:00
if not validate_pixels_are_not_premultiplied ( a ) :
2012-10-05 01:39:00 +02:00
fails . append ( ' %s not validly demultiplied ' % ( name ) )
2012-03-24 01:36:37 +01:00
a . save ( actual )
2012-10-02 02:41:07 +02:00
if not os . path . exists ( expected ) :
print ' generating expected test image: %s ' % expected
a . save ( expected )
2012-03-24 01:36:37 +01:00
expected_im = mapnik . Image . open ( expected )
# compare them
2012-10-02 03:51:53 +02:00
if a . tostring ( ) == expected_im . tostring ( ) :
successes . append ( name )
else :
fails . append ( ' failed comparing actual ( %s ) and expected( %s ) ' % ( actual , ' tests/python_tests/ ' + expected ) )
2012-10-16 17:16:45 +02:00
fail_im = side_by_side_image ( expected_im , a )
fail_im . save ( ' /tmp/mapnik-comp-op-test- ' + name + ' .fail.png ' )
2012-10-02 03:51:53 +02:00
eq_ ( len ( successes ) , num_ops , ' \n ' + ' \n ' . join ( fails ) )
2012-06-21 21:44:57 +02:00
b . demultiply ( )
# b will be slightly modified by pre and then de multiplication rounding errors
# TODO - write test to ensure the image is 99% the same.
#expected_b = mapnik.Image.open('./images/support/b.png')
#b.save('/tmp/mapnik-comp-op-test-original-mask.png')
#eq_(b.tostring(),expected_b.tostring(), '/tmp/mapnik-comp-op-test-original-mask.png is no longer equivalent to original mask: ./images/support/b.png')
2012-08-29 03:53:27 +02:00
def test_pre_multiply_status ( ) :
b = mapnik . Image . open ( ' ./images/support/b.png ' )
# not premultiplied yet, should appear that way
result = validate_pixels_are_not_premultiplied ( b )
eq_ ( result , True )
# not yet premultiplied therefore should return false
result = validate_pixels_are_premultiplied ( b )
eq_ ( result [ 0 ] , False )
# now actually premultiply the pixels
b . premultiply ( )
# now checking if premultiplied should succeed
result = validate_pixels_are_premultiplied ( b )
eq_ ( result [ 0 ] , True )
# should now not appear to look not premultiplied
result = validate_pixels_are_not_premultiplied ( b )
eq_ ( result , False )
# now actually demultiply the pixels
b . demultiply ( )
# should now appear demultiplied
result = validate_pixels_are_not_premultiplied ( b )
eq_ ( result , True )
def test_pre_multiply_status_of_map1 ( ) :
m = mapnik . Map ( 256 , 256 )
im = mapnik . Image ( m . width , m . height )
eq_ ( validate_pixels_are_not_premultiplied ( im ) , True )
mapnik . render ( m , im )
eq_ ( validate_pixels_are_not_premultiplied ( im ) , True )
def test_pre_multiply_status_of_map2 ( ) :
m = mapnik . Map ( 256 , 256 )
m . background = mapnik . Color ( 1 , 1 , 1 , 255 )
im = mapnik . Image ( m . width , m . height )
eq_ ( validate_pixels_are_not_premultiplied ( im ) , True )
mapnik . render ( m , im )
eq_ ( validate_pixels_are_not_premultiplied ( im ) , True )
2012-10-16 14:32:57 +02:00
def test_style_level_comp_op ( ) :
m = mapnik . Map ( 256 , 256 )
mapnik . load_map ( m , ' ../data/good_maps/style_level_comp_op.xml ' )
m . zoom_all ( )
successes = [ ]
fails = [ ]
for name in mapnik . CompositeOp . names :
# find_style returns a copy of the style object
style_markers = m . find_style ( " markers " )
style_markers . comp_op = getattr ( mapnik . CompositeOp , name )
# replace the original style with the modified one
replace_style ( m , " markers " , style_markers )
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
actual = ' /tmp/mapnik-style-comp-op- ' + name + ' .png '
expected = ' images/style-comp-op/ ' + name + ' .png '
im . save ( actual )
if not os . path . exists ( expected ) :
print ' generating expected test image: %s ' % expected
im . save ( expected )
expected_im = mapnik . Image . open ( expected )
# compare them
if im . tostring ( ) == expected_im . tostring ( ) :
successes . append ( name )
else :
fails . append ( ' failed comparing actual ( %s ) and expected( %s ) ' % ( actual , ' tests/python_tests/ ' + expected ) )
fail_im = side_by_side_image ( expected_im , im )
fail_im . save ( ' /tmp/mapnik-style-comp-op- ' + name + ' .fail.png ' )
eq_ ( len ( fails ) , 0 , ' \n ' + ' \n ' . join ( fails ) )
2012-09-01 03:16:49 +02:00
def test_style_level_opacity ( ) :
m = mapnik . Map ( 512 , 512 )
mapnik . load_map ( m , ' ../data/good_maps/style_level_opacity_and_blur.xml ' )
m . zoom_all ( )
im = mapnik . Image ( 512 , 512 )
mapnik . render ( m , im )
actual = ' /tmp/mapnik-style-level-opacity.png '
expected = ' images/support/mapnik-style-level-opacity.png '
im . save ( actual )
expected_im = mapnik . Image . open ( expected )
eq_ ( im . tostring ( ) , expected_im . tostring ( ) , ' failed comparing actual ( %s ) and expected ( %s ) ' % ( actual , ' tests/python_tests/ ' + expected ) )
2012-03-24 01:36:37 +01:00
2012-10-05 01:39:00 +02:00
def test_rounding_and_color_expectations ( ) :
m = mapnik . Map ( 1 , 1 )
m . background = mapnik . Color ( ' rgba(255,255,255,.4999999) ' )
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
# ugh 252, see: https://github.com/mapnik/mapnik/issues/1519
eq_ ( get_unique_colors ( im ) , [ ' rgba(252,252,252,127) ' ] )
m = mapnik . Map ( 1 , 1 )
m . background = mapnik . Color ( ' rgba(255,255,255,.5) ' )
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
eq_ ( get_unique_colors ( im ) , [ ' rgba(253,253,253,128) ' ] )
im_file = mapnik . Image . open ( ' ../data/images/stripes_pattern.png ' )
eq_ ( get_unique_colors ( im_file ) , [ ' rgba(0,0,0,0) ' , ' rgba(74,74,74,255) ' ] )
# should have no effect
im_file . premultiply ( )
eq_ ( get_unique_colors ( im_file ) , [ ' rgba(0,0,0,0) ' , ' rgba(74,74,74,255) ' ] )
im_file . set_alpha ( .5 )
# should have effect now that image has transparency
im_file . premultiply ( )
eq_ ( get_unique_colors ( im_file ) , [ ' rgba(0,0,0,0) ' , ' rgba(37,37,37,127) ' ] )
# should restore to original nonpremultiplied colors
im_file . demultiply ( )
eq_ ( get_unique_colors ( im_file ) , [ ' rgba(0,0,0,0) ' , ' rgba(74,74,74,127) ' ] )
def test_background_image_and_background_color ( ) :
m = mapnik . Map ( 8 , 8 )
m . background = mapnik . Color ( ' rgba(255,255,255,.5) ' )
m . background_image = ' ../data/images/stripes_pattern.png '
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
# note: data loss due to rounding as per https://github.com/mapnik/mapnik/issues/1519
# means that background will roundtrip to 253 not 255
#eq_(get_unique_colors(im),['rgba(255,255,255,128)', 'rgba(74,74,74,255)'])
eq_ ( get_unique_colors ( im ) , [ ' rgba(253,253,253,128) ' , ' rgba(74,74,74,255) ' ] )
def test_background_image_with_alpha_and_background_color ( ) :
m = mapnik . Map ( 10 , 10 )
m . background = mapnik . Color ( ' rgba(255,255,255,.5) ' )
m . background_image = ' ../data/images/yellow_half_trans.png '
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
eq_ ( get_unique_colors ( im ) , [ ' rgba(255,255,85,191) ' ] )
def test_background_image_with_alpha_and_background_color_against_composited_control ( ) :
m = mapnik . Map ( 10 , 10 )
m . background = mapnik . Color ( ' rgba(255,255,255,.5) ' )
m . background_image = ' ../data/images/yellow_half_trans.png '
im = mapnik . Image ( m . width , m . height )
mapnik . render ( m , im )
# create and composite the expected result
im1 = mapnik . Image ( 10 , 10 )
im1 . background = mapnik . Color ( ' rgba(255,255,255,.5) ' )
im1 . premultiply ( )
im2 = mapnik . Image ( 10 , 10 )
im2 . background = mapnik . Color ( ' rgba(255,255,0,.5) ' )
im2 . premultiply ( )
im1 . composite ( im2 )
im1 . demultiply ( )
# compare image rendered (compositing in `agg_renderer<T>::setup`)
# vs image composited via python bindings
raise Todo ( " looks like we need to investigate PNG rounding when saving " )
eq_ ( get_unique_colors ( im ) , get_unique_colors ( im1 ) )
2012-03-24 01:36:37 +01:00
if __name__ == " __main__ " :
setup ( )
2012-10-16 16:30:31 +02:00
run_tests ( eval ( x ) for x in dir ( ) if x . startswith ( " test_ " ) )