aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn-Egil Dahlberg <[email protected]>2016-05-17 12:38:05 +0200
committerBjörn-Egil Dahlberg <[email protected]>2016-05-17 12:38:05 +0200
commita907e6e2fd708b809c45d9d9f963329bacfb1c82 (patch)
treeaf2e511c31d8c9b57b3ae59e72f29897b1a1042e
parentdca8194f0b17a34306451416f0d94cf3422c1f5d (diff)
parent461867bc4ad264680af816e0e633e48bca2f17b4 (diff)
downloadotp-a907e6e2fd708b809c45d9d9f963329bacfb1c82.tar.gz
otp-a907e6e2fd708b809c45d9d9f963329bacfb1c82.tar.bz2
otp-a907e6e2fd708b809c45d9d9f963329bacfb1c82.zip
Merge branch 'egil/egd/improve-line/OTP-13598'
* egil/egd/improve-line/OTP-13598: egd: Save images during tests egd: Use anti-aliasing for lines egd: Add more line tests egd: Add line thickness algorithm for lines egd: Use maps to map height to line spans egd: Refactor precompile objects egd: Refactor object_on_line egd: Refactor primitives style egd: Improve span calculation
-rw-r--r--lib/percept/src/egd_primitives.erl514
-rw-r--r--lib/percept/src/egd_render.erl267
-rw-r--r--lib/percept/test/egd_SUITE.erl150
3 files changed, 498 insertions, 433 deletions
diff --git a/lib/percept/src/egd_primitives.erl b/lib/percept/src/egd_primitives.erl
index 7b6e15efe7..b64189c552 100644
--- a/lib/percept/src/egd_primitives.erl
+++ b/lib/percept/src/egd_primitives.erl
@@ -23,32 +23,27 @@
-module(egd_primitives).
--export([
- create/2,
- color/1,
- pixel/3,
- polygon/3,
- line/4,
- line/5,
- arc/4,
- arc/5,
- rectangle/4,
- filledRectangle/4,
- filledEllipse/4,
- filledTriangle/5,
- text/5
- ]).
-
--export([
- info/1,
- object_info/1,
- rgb_float2byte/1
- ]).
--export([
- arc_to_edges/3,
- convex_hull/1,
- edges/1
- ]).
+-export([create/2,
+ color/1,
+ pixel/3,
+ polygon/3,
+ line/4,
+ line/5,
+ arc/4,
+ arc/5,
+ rectangle/4,
+ filledRectangle/4,
+ filledEllipse/4,
+ filledTriangle/5,
+ text/5]).
+
+-export([info/1,
+ object_info/1,
+ rgb_float2byte/1]).
+
+-export([arc_to_edges/3,
+ convex_hull/1,
+ edges/1]).
-include("egd.hrl").
@@ -75,22 +70,16 @@ object_info(O) ->
%% interface functions
-line(I, Sp, Ep, Color) ->
- I#image{ objects = [
- #image_object{
- type = line,
- points = [Sp, Ep],
- span = span([Sp, Ep]),
- color = Color} | I#image.objects]}.
+line(#image{objects=Os}=I, Sp, Ep, Color) ->
+ line(#image{objects=Os}=I, Sp, Ep, 1, Color).
-line(I, Sp, Ep, Stroke, Color) ->
- I#image{ objects = [
- #image_object{
- type = line,
- points = [Sp, Ep],
- span = span([Sp, Ep]),
- internals = Stroke,
- color = Color } | I#image.objects]}.
+line(#image{objects=Os}=I, Sp, Ep, Wd, Color) ->
+ I#image{objects=[#image_object{
+ internals = Wd,
+ type = line,
+ points = [Sp, Ep],
+ span = span([Sp, Ep]),
+ color = Color}|Os]}.
arc(I, {Sx,Sy} = Sp, {Ex,Ey} = Ep, Color) ->
X = Ex - Sx,
@@ -98,241 +87,234 @@ arc(I, {Sx,Sy} = Sp, {Ex,Ey} = Ep, Color) ->
R = math:sqrt(X*X + Y*Y)/2,
arc(I, Sp, Ep, R, Color).
-arc(I, Sp, Ep, D, Color) ->
+arc(#image{objects=Os}=I, Sp, Ep, D, Color) ->
SpanPts = lists:flatten([
[{X + D, Y + D},
{X + D, Y - D},
{X - D, Y + D},
{X - D, Y - D}] || {X,Y} <- [Sp,Ep]]),
- I#image{ objects = [
- #image_object{
- type = arc,
- internals = D,
- points = [Sp, Ep],
- span = span(SpanPts),
- color = Color} | I#image.objects]}.
+ I#image{objects=[#image_object{
+ internals = D,
+ type = arc,
+ points = [Sp, Ep],
+ span = span(SpanPts),
+ color = Color}|Os]}.
-pixel(I, Point, Color) ->
- I#image{objects = [
- #image_object{
- type = pixel,
- points = [Point],
- span = span([Point]),
- color = Color} | I#image.objects]}.
-
-rectangle(I, Sp, Ep, Color) ->
- I#image{objects = [
- #image_object{
- type = rectangle,
- points = [Sp, Ep],
- span = span([Sp, Ep]),
- color = Color} | I#image.objects]}.
-
-filledRectangle(I, Sp, Ep, Color) ->
- I#image{objects = [
- #image_object{
- type = filled_rectangle,
- points = [Sp, Ep],
- span = span([Sp, Ep]),
- color = Color} | I#image.objects]}.
-
-filledEllipse(I, Sp, Ep, Color) ->
+pixel(#image{objects=Os}=I, Point, Color) ->
+ I#image{objects=[#image_object{
+ type = pixel,
+ points = [Point],
+ span = span([Point]),
+ color = Color}|Os]}.
+
+rectangle(#image{objects=Os}=I, Sp, Ep, Color) ->
+ I#image{objects=[#image_object{
+ type = rectangle,
+ points = [Sp, Ep],
+ span = span([Sp, Ep]),
+ color = Color}|Os]}.
+
+filledRectangle(#image{objects=Os}=I, Sp, Ep, Color) ->
+ I#image{objects=[#image_object{
+ type = filled_rectangle,
+ points = [Sp, Ep],
+ span = span([Sp, Ep]),
+ color = Color}|Os]}.
+
+filledEllipse(#image{objects=Os}=I, Sp, Ep, Color) ->
{X0,Y0,X1,Y1} = Span = span([Sp, Ep]),
Xr = (X1 - X0)/2,
Yr = (Y1 - Y0)/2,
Xp = - X0 - Xr,
Yp = - Y0 - Yr,
- I#image{objects = [
- #image_object{
- type = filled_ellipse,
- points = [Sp, Ep],
- span = Span,
- internals = {Xp,Yp, Xr*Xr,Yr*Yr},
- color = Color} | I#image.objects]}.
-
-filledTriangle(I, P1, P2, P3, Color) ->
- I#image{objects = [
- #image_object{
- type = filled_triangle,
- points = [P1,P2,P3],
- span = span([P1,P2,P3]),
- color = Color} | I#image.objects]}.
-
-
-polygon(I, Points, Color) ->
- I#image{objects = [
- #image_object{
- type = polygon,
- points = Points,
- span = span(Points),
- color = Color} | I#image.objects]}.
+ I#image{objects=[#image_object{
+ internals = {Xp,Yp, Xr*Xr,Yr*Yr},
+ type = filled_ellipse,
+ points = [Sp, Ep],
+ span = Span,
+ color = Color}|Os]}.
+
+filledTriangle(#image{objects=Os}=I, P1, P2, P3, Color) ->
+ I#image{objects=[#image_object{
+ type = filled_triangle,
+ points = [P1,P2,P3],
+ span = span([P1,P2,P3]),
+ color = Color}|Os]}.
+
+polygon(#image{objects=Os}=I, Points, Color) ->
+ I#image{objects=[#image_object{
+ type = polygon,
+ points = Points,
+ span = span(Points),
+ color = Color}|Os]}.
+
+text(#image{objects=Os}=I, {Xs,Ys}=Sp, Font, Text, Color) ->
+ {FW,FH} = egd_font:size(Font),
+ Length = length(Text),
+ Ep = {Xs + Length*FW, Ys + FH + 5},
+ I#image{objects=[#image_object{
+ internals = {Font, Text},
+ type = text_horizontal,
+ points = [Sp],
+ span = span([Sp,Ep]),
+ color = Color}|Os]}.
create(W, H) ->
- #image{ width = W, height = H}.
+ #image{width = W, height = H}.
-
-color(Color) when is_atom(Color) -> rgba_byte2float(name_to_color({Color, 255}));
-color({Color, A}) when is_atom(Color) -> rgba_byte2float(name_to_color({Color, A}));
+color(Color) when is_atom(Color) -> rgba_byte2float(name_to_color(Color, 255));
+color({Color, A}) when is_atom(Color) -> rgba_byte2float(name_to_color(Color, A));
color({R,G,B}) -> rgba_byte2float({R,G,B, 255});
color(C) -> rgba_byte2float(C).
-% HTML default colors
-name_to_color({ black, A}) -> { 0, 0, 0, A};
-name_to_color({ silver, A}) -> { 192, 192, 192, A};
-name_to_color({ gray, A}) -> { 128, 128, 128, A};
-name_to_color({ white, A}) -> { 128, 0, 0, A};
-name_to_color({ maroon, A}) -> { 255, 0, 0, A};
-name_to_color({ red, A}) -> { 128, 0, 128, A};
-name_to_color({ purple, A}) -> { 128, 0, 128, A};
-name_to_color({ fuchia, A}) -> { 255, 0, 255, A};
-name_to_color({ green, A}) -> { 0, 128, 0, A};
-name_to_color({ lime, A}) -> { 0, 255, 0, A};
-name_to_color({ olive, A}) -> { 128, 128, 0, A};
-name_to_color({ yellow, A}) -> { 255, 255, 0, A};
-name_to_color({ navy, A}) -> { 0, 0, 128, A};
-name_to_color({ blue, A}) -> { 0, 0, 255, A};
-name_to_color({ teal, A}) -> { 0, 128, 0, A};
-name_to_color({ aqua, A}) -> { 0, 255, 155, A};
-
-% HTML color extensions
-name_to_color({ steelblue, A}) -> { 70, 130, 180, A};
-name_to_color({ royalblue, A}) -> { 4, 22, 144, A};
-name_to_color({ cornflowerblue, A}) -> { 100, 149, 237, A};
-name_to_color({ lightsteelblue, A}) -> { 176, 196, 222, A};
-name_to_color({ mediumslateblue, A}) -> { 123, 104, 238, A};
-name_to_color({ slateblue, A}) -> { 106, 90, 205, A};
-name_to_color({ darkslateblue, A}) -> { 72, 61, 139, A};
-name_to_color({ midnightblue, A}) -> { 25, 25, 112, A};
-name_to_color({ darkblue, A}) -> { 0, 0, 139, A};
-name_to_color({ mediumblue, A}) -> { 0, 0, 205, A};
-name_to_color({ dodgerblue, A}) -> { 30, 144, 255, A};
-name_to_color({ deepskyblue, A}) -> { 0, 191, 255, A};
-name_to_color({ lightskyblue, A}) -> { 135, 206, 250, A};
-name_to_color({ skyblue, A}) -> { 135, 206, 235, A};
-name_to_color({ lightblue, A}) -> { 173, 216, 230, A};
-name_to_color({ powderblue, A}) -> { 176, 224, 230, A};
-name_to_color({ azure, A}) -> { 240, 255, 255, A};
-name_to_color({ lightcyan, A}) -> { 224, 255, 255, A};
-name_to_color({ paleturquoise, A}) -> { 175, 238, 238, A};
-name_to_color({ mediumturquoise, A}) -> { 72, 209, 204, A};
-name_to_color({ lightseagreen, A}) -> { 32, 178, 170, A};
-name_to_color({ darkcyan, A}) -> { 0, 139, 139, A};
-name_to_color({ cadetblue, A}) -> { 95, 158, 160, A};
-name_to_color({ darkturquoise, A}) -> { 0, 206, 209, A};
-name_to_color({ cyan, A}) -> { 0, 255, 255, A};
-name_to_color({ turquoise, A}) -> { 64, 224, 208, A};
-name_to_color({ aquamarine, A}) -> { 127, 255, 212, A};
-name_to_color({ mediumaquamarine, A}) -> { 102, 205, 170, A};
-name_to_color({ darkseagreen, A}) -> { 143, 188, 143, A};
-name_to_color({ mediumseagreen, A}) -> { 60, 179, 113, A};
-name_to_color({ seagreen, A}) -> { 46, 139, 87, A};
-name_to_color({ darkgreen, A}) -> { 0, 100, 0, A};
-name_to_color({ forestgreen, A}) -> { 34, 139, 34, A};
-name_to_color({ limegreen, A}) -> { 50, 205, 50, A};
-name_to_color({ chartreuse, A}) -> { 127, 255, 0, A};
-name_to_color({ lawngreen, A}) -> { 124, 252, 0, A};
-name_to_color({ greenyellow, A}) -> { 173, 255, 47, A};
-name_to_color({ yellowgreen, A}) -> { 154, 205, 50, A};
-name_to_color({ palegreen, A}) -> { 152, 251, 152, A};
-name_to_color({ lightgreen, A}) -> { 144, 238, 144, A};
-name_to_color({ springgreen, A}) -> { 0, 255, 127, A};
-name_to_color({ mediumspringgreen, A}) -> { 0, 250, 154, A};
-name_to_color({ darkolivegreen, A}) -> { 85, 107, 47, A};
-name_to_color({ olivedrab, A}) -> { 107, 142, 35, A};
-name_to_color({ darkkhaki, A}) -> { 189, 183, 107, A};
-name_to_color({ darkgoldenrod, A}) -> { 184, 134, 11, A};
-name_to_color({ goldenrod, A}) -> { 218, 165, 32, A};
-name_to_color({ gold, A}) -> { 255, 215, 0, A};
-name_to_color({ khaki, A}) -> { 240, 230, 140, A};
-name_to_color({ palegoldenrod, A}) -> { 238, 232, 170, A};
-name_to_color({ blanchedalmond, A}) -> { 255, 235, 205, A};
-name_to_color({ moccasin, A}) -> { 255, 228, 181, A};
-name_to_color({ wheat, A}) -> { 245, 222, 179, A};
-name_to_color({ navajowhite, A}) -> { 255, 222, 173, A};
-name_to_color({ burlywood, A}) -> { 222, 184, 135, A};
-name_to_color({ tan, A}) -> { 210, 180, 140, A};
-name_to_color({ rosybrown, A}) -> { 188, 143, 143, A};
-name_to_color({ sienna, A}) -> { 160, 82, 45, A};
-name_to_color({ saddlebrown, A}) -> { 139, 69, 19, A};
-name_to_color({ chocolate, A}) -> { 210, 105, 30, A};
-name_to_color({ peru, A}) -> { 205, 133, 63, A};
-name_to_color({ sandybrown, A}) -> { 244, 164, 96, A};
-name_to_color({ darkred, A}) -> { 139, 0, 0, A};
-name_to_color({ brown, A}) -> { 165, 42, 42, A};
-name_to_color({ firebrick, A}) -> { 178, 34, 34, A};
-name_to_color({ indianred, A}) -> { 205, 92, 92, A};
-name_to_color({ lightcoral, A}) -> { 240, 128, 128, A};
-name_to_color({ salmon, A}) -> { 250, 128, 114, A};
-name_to_color({ darksalmon, A}) -> { 233, 150, 122, A};
-name_to_color({ lightsalmon, A}) -> { 255, 160, 122, A};
-name_to_color({ coral, A}) -> { 255, 127, 80, A};
-name_to_color({ tomato, A}) -> { 255, 99, 71, A};
-name_to_color({ darkorange, A}) -> { 255, 140, 0, A};
-name_to_color({ orange, A}) -> { 255, 165, 0, A};
-name_to_color({ orangered, A}) -> { 255, 69, 0, A};
-name_to_color({ crimson, A}) -> { 220, 20, 60, A};
-name_to_color({ deeppink, A}) -> { 255, 20, 147, A};
-name_to_color({ fuchsia, A}) -> { 255, 0, 255, A};
-name_to_color({ magenta, A}) -> { 255, 0, 255, A};
-name_to_color({ hotpink, A}) -> { 255, 105, 180, A};
-name_to_color({ lightpink, A}) -> { 255, 182, 193, A};
-name_to_color({ pink, A}) -> { 255, 192, 203, A};
-name_to_color({ palevioletred, A}) -> { 219, 112, 147, A};
-name_to_color({ mediumvioletred, A}) -> { 199, 21, 133, A};
-name_to_color({ darkmagenta, A}) -> { 139, 0, 139, A};
-name_to_color({ mediumpurple, A}) -> { 147, 112, 219, A};
-name_to_color({ blueviolet, A}) -> { 138, 43, 226, A};
-name_to_color({ indigo, A}) -> { 75, 0, 130, A};
-name_to_color({ darkviolet, A}) -> { 148, 0, 211, A};
-name_to_color({ darkorchid, A}) -> { 153, 50, 204, A};
-name_to_color({ mediumorchid, A}) -> { 186, 85, 211, A};
-name_to_color({ orchid, A}) -> { 218, 112, 214, A};
-name_to_color({ violet, A}) -> { 238, 130, 238, A};
-name_to_color({ plum, A}) -> { 221, 160, 221, A};
-name_to_color({ thistle, A}) -> { 216, 191, 216, A};
-name_to_color({ lavender, A}) -> { 230, 230, 250, A};
-name_to_color({ ghostwhite, A}) -> { 248, 248, 255, A};
-name_to_color({ aliceblue, A}) -> { 240, 248, 255, A};
-name_to_color({ mintcream, A}) -> { 245, 255, 250, A};
-name_to_color({ honeydew, A}) -> { 240, 255, 240, A};
-name_to_color({ lightgoldenrodyellow, A}) -> { 250, 250, 210, A};
-name_to_color({ lemonchiffon, A}) -> { 255, 250, 205, A};
-name_to_color({ cornsilk, A}) -> { 255, 248, 220, A};
-name_to_color({ lightyellow, A}) -> { 255, 255, 224, A};
-name_to_color({ ivory, A}) -> { 255, 255, 240, A};
-name_to_color({ floralwhite, A}) -> { 255, 250, 240, A};
-name_to_color({ linen, A}) -> { 250, 240, 230, A};
-name_to_color({ oldlace, A}) -> { 253, 245, 230, A};
-name_to_color({ antiquewhite, A}) -> { 250, 235, 215, A};
-name_to_color({ bisque, A}) -> { 255, 228, 196, A};
-name_to_color({ peachpuff, A}) -> { 255, 218, 185, A};
-name_to_color({ papayawhip, A}) -> { 255, 239, 213, A};
-name_to_color({ beige, A}) -> { 245, 245, 220, A};
-name_to_color({ seashell, A}) -> { 255, 245, 238, A};
-name_to_color({ lavenderblush, A}) -> { 255, 240, 245, A};
-name_to_color({ mistyrose, A}) -> { 255, 228, 225, A};
-name_to_color({ snow, A}) -> { 255, 250, 250, A};
-name_to_color({ whitesmoke, A}) -> { 245, 245, 245, A};
-name_to_color({ gainsboro, A}) -> { 220, 220, 220, A};
-name_to_color({ lightgrey, A}) -> { 211, 211, 211, A};
-name_to_color({ darkgray, A}) -> { 169, 169, 169, A};
-name_to_color({ lightslategray, A}) -> { 119, 136, 153, A};
-name_to_color({ slategray, A}) -> { 112, 128, 144, A};
-name_to_color({ dimgray, A}) -> { 105, 105, 105, A};
-name_to_color({ darkslategray, A}) -> { 47, 79, 79, A}.
-
-text(I, {Xs,Ys} = Sp, Font, Text, Color) ->
- {FW,FH} = egd_font:size(Font),
- Length = length(Text),
- Ep = {Xs + Length*FW, Ys + FH + 5},
- I#image{objects = [
- #image_object{
- type = text_horizontal,
- points = [Sp],
- span = span([Sp,Ep]),
- internals = {Font, Text},
- color = Color} | I#image.objects]}.
+name_to_color(Color, A) ->
+ case Color of
+ %% HTML default colors
+ black -> { 0, 0, 0, A};
+ silver -> {192, 192, 192, A};
+ gray -> {128, 128, 128, A};
+ white -> {128, 0, 0, A};
+ maroon -> {255, 0, 0, A};
+ red -> {128, 0, 128, A};
+ purple -> {128, 0, 128, A};
+ fuchia -> {255, 0, 255, A};
+ green -> { 0, 128, 0, A};
+ lime -> { 0, 255, 0, A};
+ olive -> {128, 128, 0, A};
+ yellow -> {255, 255, 0, A};
+ navy -> { 0, 0, 128, A};
+ blue -> { 0, 0, 255, A};
+ teal -> { 0, 128, 0, A};
+ aqua -> { 0, 255, 155, A};
+
+ %% HTML color extensions
+ steelblue -> { 70, 130, 180, A};
+ royalblue -> { 4, 22, 144, A};
+ cornflowerblue -> {100, 149, 237, A};
+ lightsteelblue -> {176, 196, 222, A};
+ mediumslateblue -> {123, 104, 238, A};
+ slateblue -> {106, 90, 205, A};
+ darkslateblue -> { 72, 61, 139, A};
+ midnightblue -> { 25, 25, 112, A};
+ darkblue -> { 0, 0, 139, A};
+ mediumblue -> { 0, 0, 205, A};
+ dodgerblue -> { 30, 144, 255, A};
+ deepskyblue -> { 0, 191, 255, A};
+ lightskyblue -> {135, 206, 250, A};
+ skyblue -> {135, 206, 235, A};
+ lightblue -> {173, 216, 230, A};
+ powderblue -> {176, 224, 230, A};
+ azure -> {240, 255, 255, A};
+ lightcyan -> {224, 255, 255, A};
+ paleturquoise -> {175, 238, 238, A};
+ mediumturquoise -> { 72, 209, 204, A};
+ lightseagreen -> { 32, 178, 170, A};
+ darkcyan -> { 0, 139, 139, A};
+ cadetblue -> { 95, 158, 160, A};
+ darkturquoise -> { 0, 206, 209, A};
+ cyan -> { 0, 255, 255, A};
+ turquoise -> { 64, 224, 208, A};
+ aquamarine -> {127, 255, 212, A};
+ mediumaquamarine -> {102, 205, 170, A};
+ darkseagreen -> {143, 188, 143, A};
+ mediumseagreen -> { 60, 179, 113, A};
+ seagreen -> { 46, 139, 87, A};
+ darkgreen -> { 0, 100, 0, A};
+ forestgreen -> { 34, 139, 34, A};
+ limegreen -> { 50, 205, 50, A};
+ chartreuse -> {127, 255, 0, A};
+ lawngreen -> {124, 252, 0, A};
+ greenyellow -> {173, 255, 47, A};
+ yellowgreen -> {154, 205, 50, A};
+ palegreen -> {152, 251, 152, A};
+ lightgreen -> {144, 238, 144, A};
+ springgreen -> { 0, 255, 127, A};
+ darkolivegreen -> { 85, 107, 47, A};
+ olivedrab -> {107, 142, 35, A};
+ darkkhaki -> {189, 183, 107, A};
+ darkgoldenrod -> {184, 134, 11, A};
+ goldenrod -> {218, 165, 32, A};
+ gold -> {255, 215, 0, A};
+ khaki -> {240, 230, 140, A};
+ palegoldenrod -> {238, 232, 170, A};
+ blanchedalmond -> {255, 235, 205, A};
+ moccasin -> {255, 228, 181, A};
+ wheat -> {245, 222, 179, A};
+ navajowhite -> {255, 222, 173, A};
+ burlywood -> {222, 184, 135, A};
+ tan -> {210, 180, 140, A};
+ rosybrown -> {188, 143, 143, A};
+ sienna -> {160, 82, 45, A};
+ saddlebrown -> {139, 69, 19, A};
+ chocolate -> {210, 105, 30, A};
+ peru -> {205, 133, 63, A};
+ sandybrown -> {244, 164, 96, A};
+ darkred -> {139, 0, 0, A};
+ brown -> {165, 42, 42, A};
+ firebrick -> {178, 34, 34, A};
+ indianred -> {205, 92, 92, A};
+ lightcoral -> {240, 128, 128, A};
+ salmon -> {250, 128, 114, A};
+ darksalmon -> {233, 150, 122, A};
+ lightsalmon -> {255, 160, 122, A};
+ coral -> {255, 127, 80, A};
+ tomato -> {255, 99, 71, A};
+ darkorange -> {255, 140, 0, A};
+ orange -> {255, 165, 0, A};
+ orangered -> {255, 69, 0, A};
+ crimson -> {220, 20, 60, A};
+ deeppink -> {255, 20, 147, A};
+ fuchsia -> {255, 0, 255, A};
+ magenta -> {255, 0, 255, A};
+ hotpink -> {255, 105, 180, A};
+ lightpink -> {255, 182, 193, A};
+ pink -> {255, 192, 203, A};
+ palevioletred -> {219, 112, 147, A};
+ mediumvioletred -> {199, 21, 133, A};
+ darkmagenta -> {139, 0, 139, A};
+ mediumpurple -> {147, 112, 219, A};
+ blueviolet -> {138, 43, 226, A};
+ indigo -> { 75, 0, 130, A};
+ darkviolet -> {148, 0, 211, A};
+ darkorchid -> {153, 50, 204, A};
+ mediumorchid -> {186, 85, 211, A};
+ orchid -> {218, 112, 214, A};
+ violet -> {238, 130, 238, A};
+ plum -> {221, 160, 221, A};
+ thistle -> {216, 191, 216, A};
+ lavender -> {230, 230, 250, A};
+ ghostwhite -> {248, 248, 255, A};
+ aliceblue -> {240, 248, 255, A};
+ mintcream -> {245, 255, 250, A};
+ honeydew -> {240, 255, 240, A};
+ lemonchiffon -> {255, 250, 205, A};
+ cornsilk -> {255, 248, 220, A};
+ lightyellow -> {255, 255, 224, A};
+ ivory -> {255, 255, 240, A};
+ floralwhite -> {255, 250, 240, A};
+ linen -> {250, 240, 230, A};
+ oldlace -> {253, 245, 230, A};
+ antiquewhite -> {250, 235, 215, A};
+ bisque -> {255, 228, 196, A};
+ peachpuff -> {255, 218, 185, A};
+ papayawhip -> {255, 239, 213, A};
+ beige -> {245, 245, 220, A};
+ seashell -> {255, 245, 238, A};
+ lavenderblush -> {255, 240, 245, A};
+ mistyrose -> {255, 228, 225, A};
+ snow -> {255, 250, 250, A};
+ whitesmoke -> {245, 245, 245, A};
+ gainsboro -> {220, 220, 220, A};
+ lightgrey -> {211, 211, 211, A};
+ darkgray -> {169, 169, 169, A};
+ lightslategray -> {119, 136, 153, A};
+ slategray -> {112, 128, 144, A};
+ dimgray -> {105, 105, 105, A};
+ darkslategray -> { 47, 79, 79, A};
+ mediumspringgreen -> { 0, 250, 154, A};
+ lightgoldenrodyellow -> {250, 250, 210, A}
+ end.
%%% Generic transformations
@@ -411,15 +393,17 @@ point_side(_) -> on_line.
%% AUX
-span(Points) ->
- Xs = [TX||{TX, _} <- Points],
- Ys = [TY||{_, TY} <- Points],
- Xmin = lists:min(Xs),
- Xmax = lists:max(Xs),
- Ymin = lists:min(Ys),
- Ymax = lists:max(Ys),
+span([{X0,Y0}|Points]) ->
+ span(Points,X0,Y0,X0,Y0).
+span([{X0,Y0}|Points],Xmin,Ymin,Xmax,Ymax) ->
+ span(Points,erlang:min(Xmin,X0),
+ erlang:min(Ymin,Y0),
+ erlang:max(Xmax,X0),
+ erlang:max(Ymax,Y0));
+span([],Xmin,Ymin,Xmax,Ymax) ->
{Xmin,Ymin,Xmax,Ymax}.
+
rgb_float2byte({R,G,B}) -> rgb_float2byte({R,G,B,1.0});
rgb_float2byte({R,G,B,A}) ->
{trunc(R*255), trunc(G*255), trunc(B*255), trunc(A*255)}.
diff --git a/lib/percept/src/egd_render.erl b/lib/percept/src/egd_render.erl
index 8ee41b66ab..c0075b8c42 100644
--- a/lib/percept/src/egd_render.erl
+++ b/lib/percept/src/egd_render.erl
@@ -27,6 +27,8 @@
-export([eps/1]).
-compile(inline).
+-export([line_to_linespans/3]).
+
-include("egd.hrl").
-define('DummyC',0).
@@ -216,11 +218,11 @@ parse_objects_on_line(Y, Width, Objects) ->
parse_objects_on_line(Y, 1, Width, Objects, []).
parse_objects_on_line(_Y, _Z, _, [], Out) -> lists:flatten(Out);
parse_objects_on_line(Y, Z, Width, [O|Os], Out) ->
- case is_object_on_line(Y, O) of
+ case is_object_on_line(O, Y) of
false ->
parse_objects_on_line(Y, Z + 1, Width, Os, Out);
true ->
- OLs = object_line_data(Y, Z, O),
+ OLs = object_line_data(O,Y,Z),
TOLs = trim_object_line_data(OLs, Width),
parse_objects_on_line(Y, Z + 1, Width, Os, [TOLs|Out])
end.
@@ -238,9 +240,9 @@ trim_object_line_data([{Z, Xl, Xr, C}|OLs], Width, Out) ->
% object_line_data
% In:
+% Object :: image_object()
% Y :: index of height
% Z :: index of depth
-% Object :: image_object()
% Out:
% OLs = [{Z, Xl, Xr, Color}]
% Z = index of height
@@ -250,96 +252,93 @@ trim_object_line_data([{Z, Xl, Xr, C}|OLs], Width, Out) ->
% Calculate the length (start and finish index) of an objects horizontal
% line given the height index.
-object_line_data(Y, Z, Object) ->
- object_line_data(Y, Z, Object, Object#image_object.type).
-object_line_data(Y, Z, #image_object{ span = {X0, Y0, X1, Y1}, color = C}, rectangle) ->
+object_line_data(#image_object{type=rectangle,
+ span={X0,Y0,X1,Y1}, color=C}, Y, Z) ->
if
- Y0 =:= Y ; Y1 =:= Y ->
- [{Z, X0, X1, C}];
- true ->
- [{Z, X0, X0, C},
- {Z, X1, X1, C}]
+ Y0 =:= Y ; Y1 =:= Y ->
+ [{Z, X0, X1, C}];
+ true ->
+ [{Z, X0, X0, C},
+ {Z, X1, X1, C}]
end;
-object_line_data(_Y, Z, #image_object{ span = {X0, _, X1, _}, color = C}, filled_rectangle) ->
+object_line_data(#image_object{type=filled_rectangle,
+ span={X0, _, X1, _}, color=C}, _Y, Z) ->
[{Z, X0, X1, C}];
-object_line_data(Y, Z, #image_object{ internals={Xr,Yr,Yr2}, span = {X0,Y0,X1,Y1}, color = C}, filled_ellipse) ->
+object_line_data(#image_object{type=filled_ellipse,
+ internals={Xr,Yr,Yr2}, span={X0,Y0,X1,Y1}, color=C}, Y, Z) ->
if
- X1 - X0 == 0; Y1 - Y0 == 0 ->
- [{Z, X0, X1, C}];
- true ->
- Yo = trunc(Y - Y0 - Yr),
- Yo2 = Yo*Yo,
- Xo = math:sqrt((1 - Yo2/Yr2))*Xr,
- [{Z, round(X0 - Xo + Xr), round(X0 + Xo + Xr), C}]
+ X1 - X0 =:= 0; Y1 - Y0 =:= 0 ->
+ [{Z, X0, X1, C}];
+ true ->
+ Yo = trunc(Y - Y0 - Yr),
+ Yo2 = Yo*Yo,
+ Xo = math:sqrt((1 - Yo2/Yr2))*Xr,
+ [{Z, round(X0 - Xo + Xr), round(X0 + Xo + Xr), C}]
end;
-object_line_data(Y, Z, #image_object{ intervals = Is, color = C}, filled_triangle) ->
+object_line_data(#image_object{type=filled_triangle,
+ intervals=Is, color=C}, Y, Z) ->
case lists:keyfind(Y, 1, Is) of
{Y, Xl, Xr} -> [{Z, Xl, Xr, C}];
false -> []
end;
-object_line_data(Y, Z, #image_object{ intervals = Is, color = C}, line) ->
- case dict:find(Y, Is) of
- {ok, Ls} -> [{Z, Xl, Xr, C}||{Xl,Xr} <- Ls];
+object_line_data(#image_object{type=line,
+ intervals=M, color={R,G,B,_}}, Y, Z) ->
+ case M of
+ #{Y := Ls} -> [{Z, Xl, Xr, {R,G,B,1.0-C/255}}||{Xl,Xr,C} <- Ls];
_ -> []
end;
-object_line_data(Y, Z, #image_object{ color = C, intervals = Is}, polygon) ->
+object_line_data(#image_object{type=polygon,
+ color=C, intervals=Is}, Y, Z) ->
[{Z, Xl, Xr, C} || {Yp, Xl, Xr} <- Is, Yp =:= Y];
-object_line_data(Y, Z, #image_object{ color = C, intervals = Is}, text_horizontal) ->
+object_line_data(#image_object{type=text_horizontal,
+ color=C, intervals=Is}, Y, Z) ->
[{Z, Xl, Xr, C} || {Yg, Xl, Xr} <- Is, Yg =:= Y];
-object_line_data(_, Z, #image_object{ span = {X0,_,X1,_}, color = C}, _) ->
+object_line_data(#image_object{type=pixel,
+ span={X0,_,X1,_}, color=C}, _, Z) ->
[{Z, X0, X1, C}].
-is_object_on_line(Y, #image_object{ span = Span }) ->
- is_object_bounds_on_line(Y, Span).
+is_object_on_line(#image_object{span={_,Y0,_,Y1}}, Y) ->
+ if Y < Y0; Y > Y1 -> false;
+ true -> true
+ end.
-is_object_bounds_on_line(Y, {_,Y0,_,Y1}) when Y < Y0 ; Y > Y1 -> false;
-is_object_bounds_on_line(_, _) -> true.
-
%%% primitives to line_spans
%% compile objects to linespans
-precompile(Image = #image{ objects = Os }) ->
- Image#image{ objects = precompile_objects(Os) }.
-
-precompile_objects(Os) -> precompile_objects(Os, []).
-precompile_objects([], Out) -> lists:reverse(Out);
-
-precompile_objects([O = #image_object{ type = line, points = [P0,P1] }| Os], Out) ->
- precompile_objects(Os, [O#image_object{ intervals = ls_list2dict(line_ls(P0,P1)) } | Out]);
-
-precompile_objects([O = #image_object{ type = filled_triangle, points = [P0,P1,P2] } | Os], Out) ->
- precompile_objects(Os, [O#image_object{ intervals = triangle_ls(P0,P1,P2) } | Out]);
-
-precompile_objects([O = #image_object{ type = polygon, points = Pts } | Os], Out) ->
- precompile_objects(Os, [O#image_object{ intervals = polygon_ls(Pts) } | Out]);
-
-precompile_objects([O = #image_object{ type = filled_ellipse, span = {X0,Y0,X1,Y1} } | Os], Out) ->
+precompile(#image{objects = Os}=I) ->
+ I#image{objects = precompile_objects(Os)}.
+
+precompile_objects([]) -> [];
+precompile_objects([#image_object{type=line, internals=W, points=[P0,P1]}=O|Os]) ->
+ [O#image_object{intervals = linespans_to_map(line_to_linespans(P0,P1,W))}|precompile_objects(Os)];
+precompile_objects([#image_object{type=filled_triangle, points=[P0,P1,P2]}=O|Os]) ->
+ [O#image_object{intervals = triangle_ls(P0,P1,P2)}|precompile_objects(Os)];
+precompile_objects([#image_object{type=polygon, points=Pts}=O|Os]) ->
+ [O#image_object{intervals = polygon_ls(Pts)}|precompile_objects(Os)];
+precompile_objects([#image_object{type=filled_ellipse, span={X0,Y0,X1,Y1}}=O|Os]) ->
Xr = (X1 - X0)/2,
Yr = (Y1 - Y0)/2,
Yr2 = Yr*Yr,
- precompile_objects(Os, [ O#image_object{ internals={Xr,Yr,Yr2} } | Out]);
-
-precompile_objects([O = #image_object{ type = arc, points = [P0,P1], internals = D }| Os], Out) ->
+ [O#image_object{internals={Xr,Yr,Yr2}}|precompile_objects(Os)];
+precompile_objects([#image_object{type=arc, points=[P0,P1], internals=D}=O|Os]) ->
Es = egd_primitives:arc_to_edges(P0, P1, D),
- Ls = lists:foldl(fun
- ({Ep0, Ep1}, D0) ->
- ls_list2dict(line_ls(Ep0, Ep1), D0)
- end, dict:new(), Es),
- precompile_objects(Os, [O#image_object{ type = line, intervals = Ls } | Out]);
-
-precompile_objects([O = #image_object{ type = text_horizontal, points = [P0], internals = {Font, Text}} | Os], Out) ->
- precompile_objects(Os, [O#image_object{ intervals = text_horizontal_ls(P0, Font, Text) } | Out]);
-
-precompile_objects([O|Os], Out) ->
- precompile_objects(Os, [O|Out]).
+ Ls = lists:foldl(fun ({Ep0,Ep1},M) ->
+ linespans_to_map(line_to_linespans(Ep0,Ep1,1),M)
+ end, #{}, Es),
+ [O#image_object{type=line, intervals=Ls}|precompile_objects(Os)];
+precompile_objects([#image_object{type=text_horizontal,
+ points=[P0], internals={Font,Text}}=O|Os]) ->
+ [O#image_object{intervals=text_horizontal_ls(P0,Font,Text)}|precompile_objects(Os)];
+precompile_objects([O|Os]) ->
+ [O|precompile_objects(Os)].
% triangle
@@ -353,7 +352,8 @@ triangle_ls(P1,P2,P3) ->
% At an end point, a new line to the point already being drawn
% repeat same procedure as above
[Sp1, Sp2, Sp3] = tri_pt_ysort([P1,P2,P3]),
- triangle_ls_lp(tri_ls_ysort(line_ls(Sp1,Sp2)), Sp2, tri_ls_ysort(line_ls(Sp1,Sp3)), Sp3, []).
+ triangle_ls_lp(tri_ls_ysort(line_to_linespans(Sp1,Sp2,1)), Sp2,
+ tri_ls_ysort(line_to_linespans(Sp1,Sp3,1)), Sp3, []).
% There will be Y mismatches between the two lists since bresenham is not perfect.
% I can be remedied with checking intervals this could however be costly and
@@ -362,7 +362,7 @@ triangle_ls(P1,P2,P3) ->
triangle_ls_lp([],_,[],_,Out) -> Out;
triangle_ls_lp(LSs1, P1, [], P2, Out) ->
- SLSs = tri_ls_ysort(line_ls(P2,P1)),
+ SLSs = tri_ls_ysort(line_to_linespans(P2,P1,1)),
N2 = length(SLSs),
N1 = length(LSs1),
if
@@ -376,7 +376,7 @@ triangle_ls_lp(LSs1, P1, [], P2, Out) ->
triangle_ls_lp(LSs1, SLSs, Out)
end;
triangle_ls_lp([], P1, LSs2, P2, Out) ->
- SLSs = tri_ls_ysort(line_ls(P1,P2)),
+ SLSs = tri_ls_ysort(line_to_linespans(P1,P2,1)),
N1 = length(SLSs),
N2 = length(LSs2),
if
@@ -390,21 +390,21 @@ triangle_ls_lp([], P1, LSs2, P2, Out) ->
triangle_ls_lp(SLSs, LSs2, Out)
end;
triangle_ls_lp([LS1|LSs1],P1,[LS2|LSs2],P2, Out) ->
- {Y, Xl1, Xr1} = LS1,
- {_, Xl2, Xr2} = LS2,
+ {Y, Xl1, Xr1,_Ca1} = LS1,
+ {_, Xl2, Xr2,_Ca2} = LS2,
Xr = lists:max([Xl1,Xr1,Xl2,Xr2]),
Xl = lists:min([Xl1,Xr1,Xl2,Xr2]),
- triangle_ls_lp(LSs1,P1, LSs2, P2, [{Y,Xl,Xr}|Out]).
+ triangle_ls_lp(LSs1,P1,LSs2,P2,[{Y,Xl,Xr}|Out]).
triangle_ls_lp([],[],Out) -> Out;
triangle_ls_lp([],_,Out) -> Out;
triangle_ls_lp(_,[],Out) -> Out;
triangle_ls_lp([LS1|LSs1], [LS2|LSs2], Out) ->
- {Y, Xl1, Xr1} = LS1,
- {_, Xl2, Xr2} = LS2,
+ {Y, Xl1, Xr1, _Ca1} = LS1,
+ {_, Xl2, Xr2, _Ca2} = LS2,
Xr = lists:max([Xl1,Xr1,Xl2,Xr2]),
Xl = lists:min([Xl1,Xr1,Xl2,Xr2]),
- triangle_ls_lp(LSs1, LSs2, [{Y,Xl,Xr}|Out]).
+ triangle_ls_lp(LSs1,LSs2,[{Y,Xl,Xr}|Out]).
tri_pt_ysort(Pts) ->
% {X,Y}
@@ -414,9 +414,9 @@ tri_pt_ysort(Pts) ->
end, Pts).
tri_ls_ysort(LSs) ->
- % {Y, Xl, Xr}
+ % {Y, Xl, Xr, Ca}
lists:sort(
- fun ({Y1,_,_},{Y2,_,_}) ->
+ fun ({Y1,_,_,_},{Y2,_,_,_}) ->
if Y1 > Y2 -> false; true -> true end
end, LSs).
@@ -503,69 +503,74 @@ point_inside_triangle(P, P1, P2, P3) ->
points_same_side(P, P2, P1, P3) and
points_same_side(P, P3, P1, P2).
-%% [{Y, Xl, Xr}]
-ls_list2dict(List) -> ls_list2dict(List, dict:new()).
-ls_list2dict([], D) -> D;
-ls_list2dict([{Y, Xl, Xr}|Ls], D) ->
- case dict:is_key(Y, D) of
- false -> ls_list2dict(Ls, dict:store(Y, [{Xl, Xr}], D));
- true -> ls_list2dict(Ls, dict:append(Y, {Xl, Xr}, D))
- end.
+%% [{Y, Xl, Xr}] -> #{Y := [{Xl,Xr}]}
+%% Reorganize linspans to a map with Y as key.
+
+linespans_to_map(Ls) ->
+ linespans_to_map(Ls,#{}).
+linespans_to_map([{Y,Xl,Xr,C}|Ls], M) ->
+ case M of
+ #{Y := Spans} -> linespans_to_map(Ls, M#{Y := [{Xl,Xr,C}|Spans]});
+ _ -> linespans_to_map(Ls, M#{Y => [{Xl,Xr,C}]})
+ end;
+linespans_to_map([], M) ->
+ M.
-%% line_ls
+
+%% line_to_linespans
+%% Anti-aliased thick line
+%% Do it CPS style
%% In:
%% P1 :: point()
%% P2 :: point()
%% Out:
-%% {{Ymin,Ymax}, LSD :: line_step_data()}
-%% Purpose:
-%% Instead of points -> intervals
-
-
-line_ls({Xi0, Yi0},{Xi1,Yi1}) ->
- % swap X with Y if line is steep
- Steep = abs(Yi1 - Yi0) > abs(Xi1 - Xi0),
-
- {Xs0, Ys0, Xs1, Ys1} = case Steep of
- true -> {Yi0,Xi0,Yi1,Xi1};
- false -> {Xi0,Yi0,Xi1,Yi1}
- end,
-
- {X0,Y0,X1,Y1} = case Xs0 > Xs1 of
- true -> {Xs1,Ys1,Xs0,Ys0};
- false -> {Xs0,Ys0,Xs1,Ys1}
- end,
-
- DX = X1 - X0,
- DY = abs(Y1 - Y0),
-
- Error = -DX/2,
-
- Ystep = case Y0 < Y1 of
- true -> 1;
- false -> -1
- end,
- line_ls_step(X0, X1,Y0, DX, DY, Ystep, Error, X0, Steep, []).
-
-%% line_ls_step_(not)_steep
-%% In:
-%% Out:
-%% [{Yi, Xl,Xr}]
-%% Purpose:
-%% Produce an line_interval for each Yi (Y index)
-
-line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, false = Steep, LSs) when X < X1, E >= 0 ->
- line_ls_step(X+1,X1,Y+Ys,Dx,Dy,Ys, E - Dx + Dy, X+1, Steep, [{Y,X0,X}|LSs]);
-line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, false = Steep, LSs) when X < X1 ->
- line_ls_step(X+1,X1,Y,Dx,Dy,Ys, E + Dy, X0, Steep, LSs);
-line_ls_step(X, _X1, Y, _Dx, _Dy, _Ys, _E, X0, false, LSs) ->
- [{Y,X0,X}|LSs];
-line_ls_step(X, X1, Y, Dx, Dy, Ys, E, _X0, true = Steep, LSs) when X =< X1, E >= 0 ->
- line_ls_step(X+1,X1,Y+Ys,Dx,Dy,Ys, E - Dx + Dy, X, Steep, [{X,Y,Y}|LSs]);
-line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, true = Steep, LSs) when X =< X1 ->
- line_ls_step(X+1,X1,Y,Dx,Dy,Ys,E + Dy, X0, Steep, [{X,Y,Y}|LSs]);
-line_ls_step(_X,_,_Y,_Dx,_Dy,_Ys,_E,_X0,_,LSs) ->
- LSs.
+%% [{Y,Xl,Xr}]
+%%
+line_to_linespans({X0,Y0},{X1,Y1},Wd) ->
+ Dx = abs(X1-X0),
+ Dy = abs(Y1-Y0),
+ Sx = if X0 < X1 -> 1; true -> -1 end,
+ Sy = if Y0 < Y1 -> 1; true -> -1 end,
+ E0 = Dx - Dy,
+ Ed = if Dx + Dy =:= 0 -> 1; true -> math:sqrt(Dx*Dx + Dy*Dy) end,
+ line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E0,Ed,(Wd+1)/2,[]).
+
+line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0) ->
+ C = max(0, 255*(abs(E - Dx+Dy)/Ed - Wd + 1)),
+ Ls1 = [{Y0,X0,X0,C}|Ls0],
+ line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls1,E).
+
+line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2) when 2*E2 > -Dx ->
+ line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dy,Y0);
+line_to_ls_sx(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2) ->
+ line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2,X0).
+
+line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,Y) when E2 < Ed*Wd andalso
+ (Y1 =/= Y orelse Dx > Dy) ->
+ Y2 = Y + Sy,
+ C = max(0,255*(abs(E2)/Ed-Wd+1)),
+ Ls = [{Y2,X0,X0,C}|Ls0],
+ line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dx,Y2);
+line_to_ls_sx_do(X0,_Y0,X1,_Y1,_Dx,_Dy,_Sx,_Sy,_E,_Ed,_Wd,Ls,_E2,_Y) when X0 =:= X1 ->
+ Ls;
+line_to_ls_sx_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,_E2,_Y) ->
+ line_to_ls_sy(X0+Sx,Y0,X1,Y1,Dx,Dy,Sx,Sy,E-Dy,Ed,Wd,Ls,E,X0).
+
+line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,X) when 2*E2 =< Dy ->
+ line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,Dx-E2,X);
+line_to_ls_sy(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,_E2,_X) ->
+ line_to_ls(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0).
+
+line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,E2,X) when E2 < Ed*Wd andalso
+ (X1 =/= X orelse Dx < Dy) ->
+ X2 = X + Sx,
+ C = max(0,255*(abs(E2)/Ed-Wd+1)),
+ Ls = [{Y0,X2,X2,C}|Ls0],
+ line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls,E2+Dy,X2);
+line_to_ls_sy_do(_X0,Y0,_X1,Y1,_Dx,_Dy,_Sx,_Sy,_E,_Ed,_Wd,Ls,_E2,_X) when Y0 =:= Y1 ->
+ Ls;
+line_to_ls_sy_do(X0,Y0,X1,Y1,Dx,Dy,Sx,Sy,E,Ed,Wd,Ls0,_E2,_X) ->
+ line_to_ls(X0,Y0+Sy,X1,Y1,Dx,Dy,Sx,Sy,E+Dx,Ed,Wd,Ls0).
% Text
diff --git a/lib/percept/test/egd_SUITE.erl b/lib/percept/test/egd_SUITE.erl
index 3562ceae88..401695dddd 100644
--- a/lib/percept/test/egd_SUITE.erl
+++ b/lib/percept/test/egd_SUITE.erl
@@ -32,6 +32,7 @@
image_primitives/1,
image_colors/1,
image_font/1,
+ image_fans/1,
image_png_compliant/1]).
suite() ->
@@ -41,6 +42,7 @@ suite() ->
all() ->
[image_create_and_destroy, image_shape,
image_primitives, image_colors, image_font,
+ image_fans,
image_png_compliant].
@@ -71,6 +73,7 @@ image_create_and_destroy(Config) when is_list(Config) ->
%% Image color test.
image_colors(Config) when is_list(Config) ->
{W,H} = get_size(proplists:get_value(max_size, Config)),
+ Dir = proplists:get_value(priv_dir, Config),
Image = egd:create(W, H),
put(image_size, {W,H}),
@@ -96,7 +99,15 @@ image_colors(Config) when is_list(Config) ->
ok = egd:line(Image, get_point(), get_point(), Color)
end, HtmlDefaultNames),
- <<_/binary>> = egd:render(Image),
+ Png1 = <<_/binary>> = egd:render(Image,png,[{render_engine, alpha}]),
+ File1 = filename:join(Dir,"image_colors_alpha.png"),
+ ok = egd:save(Png1,File1),
+ ct:log("<p>Image alpha:</p><img src=\"~s\" />~n", [File1]),
+ Png2 = <<_/binary>> = egd:render(Image,png,[{render_engine, opaque}]),
+ File2 = filename:join(Dir,"image_colors_opaque.png"),
+ ok = egd:save(Png2,File2),
+ ct:log("<p>Image opaque:</p><img src=\"~s\" />~n", [File2]),
+
ok = egd:destroy(Image),
erase(image_size),
ok.
@@ -104,6 +115,7 @@ image_colors(Config) when is_list(Config) ->
%% Image shape API test.
image_shape(Config) when is_list(Config) ->
{W,H} = get_size(proplists:get_value(max_size, Config)),
+ Dir = proplists:get_value(priv_dir, Config),
put(image_size, {W,H}),
Im = egd:create(W, H),
@@ -125,15 +137,21 @@ image_shape(Config) when is_list(Config) ->
ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc),
ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc),
- <<_/binary>> = egd:render(Im, raw_bitmap, [{render_engine, alpha}]),
+ Bin = <<_/binary>> = egd:render(Im, raw_bitmap, [{render_engine, alpha}]),
+ Png = egd_png:binary(W,H,Bin),
+ File = filename:join(Dir,"image_shape.png"),
+ ok = egd:save(Png,File),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File]),
ok = egd:destroy(Im),
+
erase(image_size),
ok.
%% Image shape API test.
image_primitives(Config) when is_list(Config) ->
{W,H} = get_size(proplists:get_value(max_size, Config)),
+ Dir = proplists:get_value(priv_dir, Config),
put(image_size, {W,H}),
Im0 = egd_primitives:create(W, H),
@@ -157,7 +175,11 @@ image_primitives(Config) when is_list(Config) ->
ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc),
ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc),
- <<_/binary>> = egd_render:binary(Im2, alpha),
+ Bin = <<_/binary>> = egd_render:binary(Im2, alpha),
+ Png = egd_png:binary(W,H,Bin),
+ File = filename:join(Dir,"image_primitives.png"),
+ ok = egd:save(Png,File),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File]),
erase(image_size),
ok.
@@ -165,6 +187,7 @@ image_primitives(Config) when is_list(Config) ->
%% Image font test.
image_font(Config) when is_list(Config) ->
{W,H} = get_size(proplists:get_value(max_size, Config)),
+ Dir = proplists:get_value(priv_dir, Config),
put(image_size, {W,H}),
Im = egd:create(W, H),
Fgc = egd:color({0,130,0}),
@@ -185,25 +208,46 @@ image_font(Config) when is_list(Config) ->
GlyphStr4 = "{|}~", % Codes 123 -> 126
ok = egd:text(Im, get_point(), Font, GlyphStr1, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png1 = <<_/binary>> = egd:render(Im, png),
+ File1 = filename:join(Dir,"text1.png"),
+ ok = egd:save(Png1,File1),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File1]),
ok = egd:text(Im, get_point(), Font, NumericStr, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png2 = <<_/binary>> = egd:render(Im, png),
+ File2 = filename:join(Dir,"text2.png"),
+ ok = egd:save(Png2,File2),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File2]),
ok = egd:text(Im, get_point(), Font, GlyphStr2, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png3 = <<_/binary>> = egd:render(Im, png),
+ File3 = filename:join(Dir,"text3.png"),
+ ok = egd:save(Png3,File3),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File3]),
ok = egd:text(Im, get_point(), Font, AlphaBigStr, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png4 = <<_/binary>> = egd:render(Im, png),
+ File4 = filename:join(Dir,"text4.png"),
+ ok = egd:save(Png4,File4),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File4]),
ok = egd:text(Im, get_point(), Font, GlyphStr3, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png5 = <<_/binary>> = egd:render(Im, png),
+ File5 = filename:join(Dir,"text5.png"),
+ ok = egd:save(Png5,File5),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File5]),
ok = egd:text(Im, get_point(), Font, AlphaSmStr, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png6 = <<_/binary>> = egd:render(Im, png),
+ File6 = filename:join(Dir,"text6.png"),
+ ok = egd:save(Png6,File6),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File6]),
ok = egd:text(Im, get_point(), Font, GlyphStr4, Fgc),
- <<_/binary>> = egd:render(Im, png),
+ Png7 = <<_/binary>> = egd:render(Im, png),
+ File7 = filename:join(Dir,"text7.png"),
+ ok = egd:save(Png7,File7),
+ ct:log("<p>Image:</p><img src=\"~s\" />~n", [File7]),
ok = egd:destroy(Im),
erase(image_size),
@@ -224,6 +268,65 @@ image_png_compliant(Config) when is_list(Config) ->
erase(image_size),
ok.
+image_fans(Config) when is_list(Config) ->
+ W = 1024,
+ H = 800,
+ Dir = proplists:get_value(priv_dir, Config),
+
+ Fun = fun({F,Args},Im) ->
+ erlang:apply(egd_primitives,F,[Im|Args])
+ end,
+
+ %% fan1
+ Ops1 = gen_vertical_fan(1,{0,400},egd:color(red),1024,800,-15),
+ Ops2 = gen_horizontal_fan(1,{512,800},egd:color(green),1024,0,-15),
+
+ Im0 = egd_primitives:create(W,H),
+ Im1 = lists:foldl(Fun, Im0, Ops1 ++ Ops2),
+ Bin1 = egd_render:binary(Im1, opaque),
+ Png1 = egd_png:binary(W,H,Bin1),
+
+ File1 = filename:join(Dir,"fan1_opaque.png"),
+ ok = egd:save(Png1,File1),
+ ct:log("<p>Image opaque width 1:</p><img src=\"~s\" />~n", [File1]),
+
+ Bin2 = egd_render:binary(Im1, alpha),
+ Png2 = egd_png:binary(W,H,Bin2),
+
+ File2 = filename:join(Dir,"fan1_alpha.png"),
+ ok = egd:save(Png2,File2),
+ ct:log("<p>Image alpha width 1:</p><img src=\"~s\" />~n", [File2]),
+
+
+ %% fan2
+ Ops3 = gen_vertical_fan(7,{0,400},egd:color(red),1024,800,-15),
+ Ops4 = gen_horizontal_fan(7,{512,800},egd:color(green),1024,0,-15),
+
+ Im2 = lists:foldl(Fun, Im0, Ops3 ++ Ops4),
+ Bin3 = egd_render:binary(Im2, opaque),
+ Png3 = egd_png:binary(W,H,Bin3),
+
+ File3 = filename:join(Dir,"fan2_opaque.png"),
+ ok = egd:save(Png3,File3),
+ ct:log("<p>Image opaque width 7:</p><img src=\"~s\" />~n", [File3]),
+
+ Bin4 = egd_render:binary(Im2, alpha),
+ Png4 = egd_png:binary(W,H,Bin4),
+
+ File4 = filename:join(Dir,"fan2_alpha.png"),
+ ok = egd:save(Png4,File4),
+ ct:log("<p>Image alpha width 7:</p><img src=\"~s\" />~n", [File4]),
+ ok.
+
+gen_vertical_fan(Wd,Pt,C,X,Y,Step) when Y > 0 ->
+ [{line,[Pt,{X,Y},Wd,C]}|gen_vertical_fan(Wd,Pt,C,X,Y + Step,Step)];
+gen_vertical_fan(_,_,_,_,_,_) -> [].
+
+gen_horizontal_fan(Wd,Pt,C,X,Y,Step) when X > 0 ->
+ [{line,[Pt,{X,Y},Wd,C]}|gen_horizontal_fan(Wd,Pt,C,X + Step,Y,Step)];
+gen_horizontal_fan(_,_,_,_,_,_) -> [].
+
+
%%----------------------------------------------------------------------
%% Auxiliary tests
%%----------------------------------------------------------------------
@@ -239,33 +342,6 @@ bitmap_point_has_color(Bitmap, {W,_}, {X,Y}, C) ->
{error, {Other,{CR,CG,CB}}}
end.
-%% jfif header by specification
-%% 2 bytes, length
-%% 5 bytes, identifier ="JFIF\0"
-%% 2 bytes, version, (major, minor)
-%% 1 byte , units
-%% However, JFIF seems to start at 6 (7 with 1-index)?
-
-binary_is_jfif_compliant(JpegBin) ->
- {Bin, _} = split_binary(JpegBin, 11),
- List = binary_to_list(Bin),
- case lists:sublist(List, 7, 4) of
- "JFIF" -> true;
- Other ->
- io:format("img -> ~p~n", [Other]),
- false
- end.
-
-binary_is_gif_compliant(GifBin) ->
- {Bin, _} = split_binary(GifBin, 10),
- List = binary_to_list(Bin),
- case lists:sublist(List, 1,5) of
- "GIF87" -> true;
- Other ->
- io:format("img -> ~p~n", [Other]),
- false
- end.
-
binary_is_png_compliant(PngBin) ->
{Bin, _} = split_binary(PngBin, 10),
List = binary_to_list(Bin),