aboutsummaryrefslogtreecommitdiffstats
path: root/lib/percept/src/percept_image.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/percept/src/percept_image.erl')
-rw-r--r--lib/percept/src/percept_image.erl315
1 files changed, 315 insertions, 0 deletions
diff --git a/lib/percept/src/percept_image.erl b/lib/percept/src/percept_image.erl
new file mode 100644
index 0000000000..5baedabecf
--- /dev/null
+++ b/lib/percept/src/percept_image.erl
@@ -0,0 +1,315 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(percept_image).
+-export([ proc_lifetime/5,
+ percentage/3,
+ graph/3,
+ graph/4,
+ activities/3,
+ activities/4]).
+-record(graph_area, {x = 0, y = 0, width, height}).
+-compile(inline).
+
+%%% -------------------------------------
+%%% GRAF
+%%% -------------------------------------
+
+%% graph(Widht, Height, Range, Data)
+
+graph(Width, Height, {RXmin, RYmin, RXmax, RYmax}, Data) ->
+ Data2 = [{X, Y1 + Y2} || {X, Y1, Y2} <- Data],
+ MinMax = percept_analyzer:minmax(Data2),
+ {Xmin, Ymin, Xmax, Ymax} = MinMax,
+ graf1(Width, Height,{ lists:min([RXmin, Xmin]),
+ lists:min([RYmin, Ymin]),
+ lists:max([RXmax, Xmax]),
+ lists:max([RYmax, Ymax])}, Data).
+
+%% graph(Widht, Height, Data) = Image
+%% In:
+%% Width = integer(),
+%% Height = integer(),
+%% Data = [{Time, Procs, Ports}]
+%% Time = float()
+%% Procs = integer()
+%% Ports = integer()
+%% Out:
+%% Image = binary()
+
+graph(Width, Height, Data) ->
+ Data2 = [{X, Y1 + Y2} || {X, Y1, Y2} <- Data],
+ Bounds = percept_analyzer:minmax(Data2),
+ graf1(Width, Height, Bounds, Data).
+
+graf1(Width, Height, {Xmin, Ymin, Xmax, Ymax}, Data) ->
+ % Calculate areas
+ HO = 20,
+ GrafArea = #graph_area{x = HO, y = 4, width = Width - 2*HO, height = Height - 17},
+ XticksArea = #graph_area{x = HO, y = Height - 13, width = Width - 2*HO, height = 13},
+ YticksArea = #graph_area{x = 1, y = 4, width = HO, height = Height - 17},
+
+ %% Initiate Image
+
+ Image = egd:create(Width, Height),
+
+ %% Set colors
+
+ Black = egd:color(Image, {0, 0, 0}),
+ ProcColor = egd:color(Image, {0, 255, 0}),
+ PortColor = egd:color(Image, {255, 0, 0}),
+
+ %% Draw graf, xticks and yticks
+ draw_graf(Image, Data, {Black, ProcColor, PortColor}, GrafArea, {Xmin, Ymin, Xmax, Ymax}),
+ draw_xticks(Image, Black, XticksArea, {Xmin, Xmax}, Data),
+ draw_yticks(Image, Black, YticksArea, {Ymin, Ymax}),
+
+ %% Kill image and return binaries
+ Binary = egd:render(Image, png),
+ egd:destroy(Image),
+ Binary.
+
+%% draw_graf(Image, Data, Color, GraphArea, DataBounds)
+%% Image, port to Image
+%% Data, list of three tuple data, (X, Y1, Y2)
+%% Color, {ForegroundColor, ProcFillColor, PortFillColor}
+%% DataBounds, {Xmin, Ymin, Xmax, Ymax}
+
+draw_graf(Im, Data, Colors, GA = #graph_area{x = X0, y = Y0, width = Width, height = Height}, {Xmin, _Ymin, Xmax, Ymax}) ->
+ Dx = (Width)/(Xmax - Xmin),
+ Dy = (Height)/(Ymax),
+ Plotdata = [{trunc(X0 + X*Dx - Xmin*Dx), trunc(Y0 + Height - Y1*Dy), trunc(Y0 + Height - (Y1 + Y2)*Dy)} || {X, Y1, Y2} <- Data],
+ draw_graf(Im, Plotdata, Colors, GA).
+
+draw_graf(Im, [{X1, Yproc1, Yport1}, {X2, Yproc2, Yport2}|Data], C, GA) when X2 - X1 < 1 ->
+ draw_graf(Im, [{X1, [{Yproc2, Yport2},{Yproc1, Yport1}]}|Data], C, GA);
+
+draw_graf(Im, [{X1, Ys1}, {X2, Yproc2, Yport2}|Data], C, GA) when X2 - X1 < 1, is_list(Ys1) ->
+ draw_graf(Im, [{X1, [{Yproc2, Yport2}|Ys1]}|Data], C, GA);
+
+draw_graf(Im, [{X1, Yproc1, Yport1}, {X2, Yproc2, Yport2}|Data], C = {B, PrC, PoC}, GA = #graph_area{y = Y0, height = H}) ->
+ GyZero = trunc(Y0 + H),
+ egd:filledRectangle(Im, {X1, GyZero}, {X2, Yproc1}, PrC),
+ egd:filledRectangle(Im, {X1, Yproc1}, {X2, Yport1}, PoC),
+ egd:line(Im, {X1, Yport1}, {X2, Yport1}, B), % top line
+ egd:line(Im, {X1, Yport2}, {X1, Yport1}, B), % right line
+ egd:line(Im, {X2, Yport1}, {X2, Yport2}, B), % right line
+ draw_graf(Im, [{X2, Yproc2, Yport2}|Data], C, GA);
+
+draw_graf(Im, [{X1, Ys1 = [{Yproc1,Yport1}|_]}, {X2, Yproc2, Yport2}|Data], C = {B, PrC, PoC}, GA = #graph_area{y = Y0, height = H}) ->
+ GyZero = trunc(Y0 + H),
+ Yprocs = [Yp || {Yp, _} <- Ys1],
+ Yports = [Yp || {_, Yp} <- Ys1],
+
+ YprMin = lists:min(Yprocs),
+ YprMax = lists:max(Yprocs),
+ YpoMax = lists:max(Yports),
+ egd:filledRectangle(Im, {X1, GyZero}, {X2, Yproc1}, PrC),
+ egd:filledRectangle(Im, {X1, Yproc1}, {X2, Yport1}, PoC),
+ egd:filledRectangle(Im, {X1, Yport1}, {X2, Yport1}, B), % top line
+ egd:filledRectangle(Im, {X2, Yport1}, {X2, Yport2}, B), % right line
+
+ egd:filledRectangle(Im, {X1, GyZero}, {X1, YprMin}, PrC), % left proc green line
+ egd:filledRectangle(Im, {X1, YprMax}, {X1, YpoMax}, PoC), % left port line
+ egd:filledRectangle(Im, {X1, YprMax}, {X1, YprMin}, B),
+
+ draw_graf(Im, [{X2, Yproc2, Yport2}|Data], C, GA);
+draw_graf(_, _, _, _) -> ok.
+
+draw_xticks(Image, Color, XticksArea, {Xmin, Xmax}, Data) ->
+ #graph_area{x = X0, y = Y0, width = Width} = XticksArea,
+
+ DX = Width/(Xmax - Xmin),
+ Offset = X0 - Xmin*DX,
+ Y = trunc(Y0),
+ Font = load_font(),
+ {FontW, _FontH} = egd_font:size(Font),
+ egd:filledRectangle(Image, {trunc(X0), Y}, {trunc(X0 + Width), Y}, Color),
+ lists:foldl(
+ fun ({X,_,_}, PX) ->
+ X1 = trunc(Offset + X*DX),
+
+ % Optimization:
+ % if offset has past half the previous text
+ % start checking this text
+
+ if
+ X1 > PX ->
+ Text = lists:flatten(io_lib:format("~.3f", [float(X)])),
+ TextLength = length(Text),
+ TextWidth = TextLength*FontW,
+ Spacing = 2,
+ if
+ X1 > PX + round(TextWidth/2) + Spacing ->
+ egd:line(Image, {X1, Y - 3}, {X1, Y + 3}, Color),
+ text(Image, {X1 - round(TextWidth/2), Y + 2}, Font, Text, Color),
+ X1 + round(TextWidth/2) + Spacing;
+ true ->
+ PX
+ end;
+ true ->
+ PX
+ end
+ end, 0, Data).
+
+draw_yticks(Im, Color, TickArea, {_,Ymax}) ->
+ #graph_area{x = X0, y = Y0, width = Width, height = Height} = TickArea,
+ Font = load_font(),
+ X = trunc(X0 + Width),
+ Dy = (Height)/(Ymax),
+ Yts = if
+ Height/(Ymax*12) < 1.0 -> round(1 + Ymax*15/Height);
+ true -> 1
+ end,
+ egd:filledRectangle(Im, {X, trunc(0 + Y0)}, {X, trunc(Y0 + Height)}, Color),
+ draw_yticks0(Im, Font, Color, 0, Yts, Ymax, {X, Height, Dy}).
+
+draw_yticks0(Im, Font, Color, Yi, Yts, Ymax, Area) when Yi < Ymax ->
+ {X, Height, Dy} = Area,
+ Y = round(Height - (Yi*Dy) + 3),
+
+ egd:filledRectangle(Im, {X - 3, Y}, {X + 3, Y}, Color),
+ Text = lists:flatten(io_lib:format("~p", [Yi])),
+ text(Im, {0, Y - 4}, Font, Text, Color),
+ draw_yticks0(Im, Font, Color, Yi + Yts, Yts, Ymax, Area);
+draw_yticks0(_, _, _, _, _, _, _) -> ok.
+
+%%% -------------------------------------
+%%% ACTIVITIES
+%%% -------------------------------------
+
+%% activities(Width, Height, Range, Activities) -> Binary
+%% In:
+%% Width = integer()
+%% Height = integer()
+%% Range = {float(), float()}
+%% Activities = [{float(), active | inactive}]
+%% Out:
+%% Binary = binary()
+
+activities(Width, Height, {UXmin, UXmax}, Activities) ->
+ Xs = [ X || {X,_} <- Activities],
+ Xmin = lists:min(Xs),
+ Xmax = lists:max(Xs),
+ activities0(Width, Height, {lists:min([Xmin, UXmin]), lists:max([UXmax, Xmax])}, Activities).
+
+activities(Width, Height, Activities) ->
+ Xs = [ X || {X,_} <- Activities],
+ Xmin = lists:min(Xs),
+ Xmax = lists:max(Xs),
+ activities0(Width, Height, {Xmin, Xmax}, Activities).
+
+activities0(Width, Height, {Xmin, Xmax}, Activities) ->
+ Image = egd:create(Width, Height),
+ Grey = egd:color(Image, {200, 200, 200}),
+ HO = 20,
+ ActivityArea = #graph_area{x = HO, y = 0, width = Width - 2*HO, height = Height},
+ egd:filledRectangle(Image, {0, 0}, {Width, Height}, Grey),
+ draw_activity(Image, {Xmin, Xmax}, ActivityArea, Activities),
+ Binary = egd:render(Image, png),
+ egd:destroy(Image),
+ Binary.
+
+draw_activity(Image, {Xmin, Xmax}, Area = #graph_area{ width = Width }, Acts) ->
+ White = egd:color({255, 255, 255}),
+ Green = egd:color({0,250, 0}),
+ Black = egd:color({0, 0, 0}),
+
+ Dx = Width/(Xmax - Xmin),
+
+ draw_activity(Image, {Xmin, Xmax}, Area, {White, Green, Black}, Dx, Acts).
+
+draw_activity(_, _, _, _, _, [_]) -> ok;
+draw_activity(Image, {Xmin, Xmax}, Area = #graph_area{ height = Height, x = X0 }, {Cw, Cg, Cb}, Dx, [{Xa1, State}, {Xa2, Act2} | Acts]) ->
+ X1 = erlang:trunc(X0 + Dx*Xa1 - Xmin*Dx),
+ X2 = erlang:trunc(X0 + Dx*Xa2 - Xmin*Dx),
+
+ case State of
+ inactive ->
+ egd:filledRectangle(Image, {X1, 0}, {X2, Height - 1}, Cw),
+ egd:rectangle(Image, {X1, 0}, {X2, Height - 1}, Cb);
+ active ->
+ egd:filledRectangle(Image, {X1, 0}, {X2, Height - 1}, Cg),
+ egd:rectangle(Image, {X1, 0}, {X2, Height - 1}, Cb)
+ end,
+ draw_activity(Image, {Xmin, Xmax}, Area, {Cw, Cg, Cb}, Dx, [{Xa2, Act2} | Acts]).
+
+
+
+%%% -------------------------------------
+%%% Process lifetime
+%%% Used by processes page
+%%% -------------------------------------
+
+proc_lifetime(Width, Height, Start, End, ProfileTime) ->
+ Im = egd:create(round(Width), round(Height)),
+ Black = egd:color(Im, {0, 0, 0}),
+ Green = egd:color(Im, {0, 255, 0}),
+
+ % Ratio and coordinates
+
+ DX = (Width-1)/ProfileTime,
+ X1 = round(DX*Start),
+ X2 = round(DX*End),
+
+ % Paint
+ egd:filledRectangle(Im, {X1, 0}, {X2, Height - 1}, Green),
+ egd:rectangle(Im, {X1, 0}, {X2, Height - 1}, Black),
+
+ Binary = egd:render(Im, png),
+ egd:destroy(Im),
+ Binary.
+
+%%% -------------------------------------
+%%% Percentage
+%%% Used by process_info page
+%%% Percentage should be 0.0 -> 1.0
+%%% -------------------------------------
+percentage(Width, Height, Percentage) ->
+ Im = egd:create(round(Width), round(Height)),
+ Font = load_font(),
+ Black = egd:color(Im, {0, 0, 0}),
+ Green = egd:color(Im, {0, 255, 0}),
+
+ % Ratio and coordinates
+
+ X = round(Width - 1 - Percentage*(Width - 1)),
+
+ % Paint
+ egd:filledRectangle(Im, {X, 0}, {Width - 1, Height - 1}, Green),
+ {FontW, _} = egd_font:size(Font),
+ String = lists:flatten(io_lib:format("~.10B %", [round(100*Percentage)])),
+
+ text( Im,
+ {round(Width/2 - (FontW*length(String)/2)), 0},
+ Font,
+ String,
+ Black),
+ egd:rectangle(Im, {X, 0}, {Width - 1, Height - 1}, Black),
+
+ Binary = egd:render(Im, png),
+ egd:destroy(Im),
+ Binary.
+
+
+load_font() ->
+ Filename = filename:join([code:priv_dir(percept),"fonts", "6x11_latin1.wingsfont"]),
+ egd_font:load(Filename).
+
+text(Image, {X,Y}, Font, Text, Color) ->
+ egd:text(Image, {X,Y-2}, Font, Text, Color).