29
29
-include("egd.hrl").
30
30
-define('DummyC',0).
32
binary(Image) -> binary(Image, opaque).
33
binary(Image, opaque).
34
35
binary(Image, Type) ->
35
36
parallel_binary(precompile(Image),Type).
37
38
parallel_binary(Image = #image{ height = Height },Type) ->
38
case lists:min([erlang:system_info(schedulers), Height]) of
39
case erlang:min(erlang:system_info(schedulers), Height) of
40
41
% if the height or the number of schedulers is 1
41
42
% do the scanlines in this process.
42
43
W = Image#image.width,
43
44
Bg = Image#image.background,
44
45
Os = Image#image.objects,
45
erlang:list_to_binary(lists:map(fun
46
(Y) -> scanline(Y, Os, {0,0,W - 1, Bg}, Type)
47
end, lists:seq(1, Height)));
46
erlang:list_to_binary([scanline(Y, Os, {0,0,W - 1, Bg}, Type)
47
|| Y <- lists:seq(1, Height)]);
49
49
Pids = start_workers(Np, Type),
50
50
Handler = handle_workers(Height, Pids),
118
124
receive_binaries(H - 1, [Bin|Bins])
122
127
scanline(Y, Os, {_,_,Width,_}=LSB, Type) ->
123
OLSs = parse_objects_on_line(Y-1, Width, Os),
124
URLSs = resulting_line_spans([LSB|OLSs],Type),
126
% FIXME: Can we keep the list sorted instead of sorting it?
128
RLSs = lists:reverse(URLSs),
130
resulting_scanline(RLSs,Width).
132
resulting_scanline(RLSs, Width) -> resulting_scanline(RLSs, Width, []).
133
resulting_scanline([], _, Scanlines) -> Scanlines;
134
resulting_scanline([{_,Xl, Xr, C} | RLSs], Width, Scanlines) ->
135
{R,G,B,_} = rgb_float2byte(C),
136
Scanline = lists:duplicate(trunc(Xr - Xl + 1), <<R:8,G:8,B:8>>),
137
resulting_scanline(RLSs, Width, [Scanline|Scanlines]).
128
OLSs = parse_objects_on_line(Y-1, Width, Os),
129
RLSs = resulting_line_spans([LSB|OLSs],Type),
130
[ lists:duplicate(Xr - Xl + 1, <<(trunc(R*255)):8,(trunc(G*255)):8,(trunc(B*255)):8>>) || {_,Xl, Xr, {R,G,B,_}} <- RLSs ].
139
132
resulting_line_spans(LSs,Type) ->
140
133
%% Build a list of "transitions" from left to right.
141
134
Trans = line_spans_to_trans(LSs),
142
135
%% Convert list of "transitions" to linespans.
143
trans_to_line_spans(Trans,Type).
136
trans_to_line_spans(Trans,Type).
145
138
line_spans_to_trans(LSs) ->
146
139
line_spans_to_trans(LSs,[],0).
192
185
color([],_) -> {0.0,0.0,0.0,0.0};
193
186
color([{_,C}|_],opaque) -> C;
194
color(Layers,alpha) -> color1({0,0,0,0},Layers).
187
color(Layers,alpha) -> color1({0.0,0.0,0.0,0.0},Layers).
196
189
color1(Color,[]) -> Color;
197
color1(Color,[{_,C}|Layers]) -> color1(blend(Color,C),Layers).
199
blend(C1,C2) -> alpha_blend(C1,C2).
190
color1(Color,[{_,C}|Layers]) -> color1(alpha_blend(Color,C),Layers).
201
192
modify_layers(Layers,[]) -> Layers;
202
modify_layers(Layers,[{{_,Z,Op},C}|Trans]) ->
203
modify_layers(case Op of
205
add_layer(Layers,Z,C);
207
remove_layer(Layers,Z,C)
193
modify_layers(Layers,[{{_,Z,start},C}|Trans]) ->
194
modify_layers(add_layer(Layers, Z, C), Trans);
195
modify_layers(Layers,[{{_,Z,stop },C}|Trans]) ->
196
modify_layers(remove_layer(Layers, Z, C), Trans).
211
198
add_layer([{Z1,_}=H|Layers],Z,C) when Z1 > Z ->
212
199
[H|add_layer(Layers,Z,C)];
240
227
trim_object_line_data(OLs, Width) ->
241
228
trim_object_line_data(OLs, Width, []).
242
229
trim_object_line_data([], _, Out) -> Out;
231
trim_object_line_data([{_, Xl, _, _}|OLs], Width, Out) when Xl > Width ->
232
trim_object_line_data(OLs, Width, Out);
233
trim_object_line_data([{_, _, Xr, _}|OLs], Width, Out) when Xr < 0 ->
234
trim_object_line_data(OLs, Width, Out);
243
235
trim_object_line_data([{Z, Xl, Xr, C}|OLs], Width, Out) ->
246
trim_object_line_data(OLs, Width, Out);
248
trim_object_line_data(OLs, Width, Out);
250
trim_object_line_data(OLs, Width, [{Z, lists:max([0,Xl]), lists:min([Xr,Width]), C}|Out])
236
trim_object_line_data(OLs, Width, [{Z, erlang:max(0,Xl), erlang:min(Xr,Width), C}|Out]).
253
238
% object_line_data
277
263
object_line_data(_Y, Z, #image_object{ span = {X0, _, X1, _}, color = C}, filled_rectangle) ->
278
264
[{Z, X0, X1, C}];
280
object_line_data(Y, Z, #image_object{ span = {X0,Y0,X1,Y1}, color = C}, filled_ellipse) ->
266
object_line_data(Y, Z, #image_object{ internals={Xr,Yr,Yr2}, span = {X0,Y0,X1,Y1}, color = C}, filled_ellipse) ->
282
X1 - X0 == 0 -> % if the width is exactly one pixel
284
X1 - X0 < 0 -> throw(bad_ellipse_width);
285
Y1 - Y0 == 0 -> % Height exactly one pixel, get width
268
X1 - X0 == 0; Y1 - Y0 == 0 ->
286
269
[{Z, X0, X1, C}];
290
Yo = trunc(Y - Y0 - Yr),
271
Yo = trunc(Y - Y0 - Yr),
293
Xo = math:sqrt((1 - Yo2/Yr2))*Xr,
273
Xo = math:sqrt((1 - Yo2/Yr2))*Xr,
294
274
[{Z, round(X0 - Xo + Xr), round(X0 + Xo + Xr), C}]
297
277
object_line_data(Y, Z, #image_object{ intervals = Is, color = C}, filled_triangle) ->
298
case lists:keysearch(Y, 1, Is) of
299
{value, {Y, Xl, Xr}} -> [{Z, Xl, Xr, C}];
278
case lists:keyfind(Y, 1, Is) of
279
{Y, Xl, Xr} -> [{Z, Xl, Xr, C}];
303
283
object_line_data(Y, Z, #image_object{ intervals = Is, color = C}, line) ->
304
284
case dict:find(Y, Is) of
305
%{ok, {Xl, Xr}} -> [{Z, Xl, Xr, C}];
306
285
{ok, Ls} -> [{Z, Xl, Xr, C}||{Xl,Xr} <- Ls];
310
object_line_data(Y, Z, O, polygon) ->
313
if Yp == Y -> true; true -> false end
314
end, O#image_object.intervals),
315
[ {Z, Xl, Xr, O#image_object.color} || {_, Xl, Xr} <- Is];
317
object_line_data(Y, Z, #image_object{ color = C, intervals = Is }, text_horizontal) ->
320
fun ({Yg,Xl,Xr}, Out) ->
323
[{Z, Xl, Xr, C}|Out];
289
object_line_data(Y, Z, #image_object{ color = C, intervals = Is}, polygon) ->
290
[{Z, Xl, Xr, C} || {Yp, Xl, Xr} <- Is, Yp =:= Y];
292
object_line_data(Y, Z, #image_object{ color = C, intervals = Is}, text_horizontal) ->
293
[{Z, Xl, Xr, C} || {Yg, Xl, Xr} <- Is, Yg =:= Y];
328
295
object_line_data(_, Z, #image_object{ span = {X0,_,X1,_}, color = C}, _) ->
330
296
[{Z, X0, X1, C}].
332
is_object_on_line(Y, Object) ->
333
is_object_bounds_on_line(Y, Object#image_object.span).
298
is_object_on_line(Y, #image_object{ span = Span }) ->
299
is_object_bounds_on_line(Y, Span).
335
is_object_bounds_on_line(Y, {_,Y0,_,Y1}) ->
342
rgb_float2byte({R,G,B,A}) ->
343
{trunc(R*255), trunc(G*255), trunc(B*255), trunc(A*255)}.
301
is_object_bounds_on_line(Y, {_,Y0,_,Y1}) when Y < Y0 ; Y > Y1 -> false;
302
is_object_bounds_on_line(_, _) -> true.
345
304
%%% primitives to line_spans
595
554
%% Produce an line_interval for each Yi (Y index)
597
% Iterating the X-axis
599
line_ls_step_not_steep({X,X1},Y,Dx,Dy,Ys,E, X0, LSs) when X < X1 ->
602
line_ls_step_not_steep({X+1,X1},Y+Ys,Dx,Dy,Ys, E - Dx + Dy, X+1,[{Y,X0,X}|LSs]);
604
line_ls_step_not_steep({X+1,X1},Y,Dx,Dy,Ys, E + Dy, X0, LSs)
606
line_ls_step_not_steep({X,_},Y,_Dx,_Dy,_Ystep,_E,X0,LSs) ->
609
% Iterating the Y-axis
610
line_ls_step_steep({X,X1},Y,Dx,Dy,Ystep,E, X0, LSs) when X =< X1 ->
613
line_ls_step_steep({X + 1,X1},Y+Ystep,Dx,Dy,Ystep,E - Dx + Dy,X,[{X,Y,Y}|LSs]);
615
line_ls_step_steep({X + 1,X1},Y,Dx,Dy,Ystep,E + Dy,X0, [{X,Y,Y}|LSs])
617
line_ls_step_steep({_X,_},_Y,_Dx,_Dy,_Ystep,_E,_X0,LSs) ->
556
line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, false = Steep, LSs) when X < X1, E >= 0 ->
557
line_ls_step(X+1,X1,Y+Ys,Dx,Dy,Ys, E - Dx + Dy, X+1, Steep, [{Y,X0,X}|LSs]);
558
line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, false = Steep, LSs) when X < X1 ->
559
line_ls_step(X+1,X1,Y,Dx,Dy,Ys, E + Dy, X0, Steep, LSs);
560
line_ls_step(X, _X1, Y, _Dx, _Dy, _Ys, _E, X0, false, LSs) ->
562
line_ls_step(X, X1, Y, Dx, Dy, Ys, E, _X0, true = Steep, LSs) when X =< X1, E >= 0 ->
563
line_ls_step(X+1,X1,Y+Ys,Dx,Dy,Ys, E - Dx + Dy, X, Steep, [{X,Y,Y}|LSs]);
564
line_ls_step(X, X1, Y, Dx, Dy, Ys, E, X0, true = Steep, LSs) when X =< X1 ->
565
line_ls_step(X+1,X1,Y,Dx,Dy,Ys,E + Dy, X0, Steep, [{X,Y,Y}|LSs]);
566
line_ls_step(_X,_,_Y,_Dx,_Dy,_Ys,_E,_X0,_,LSs) ->