Skip to content

Commit

Permalink
revert to ARGB32 imagematrix
Browse files Browse the repository at this point in the history
  • Loading branch information
cormullion committed Aug 25, 2020
1 parent 2318620 commit f14f9da
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 117 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog

## [v3.0.0] - forthcoming - September 2020
## [v2.5.0] - forthcoming - September 2020

### Added

Expand All @@ -9,7 +9,6 @@

### Changed

- return values of imagematrix have changed, now return [[1, 1, 0, 1], [1, 0, 1, 1], ...]
- docs use JuliaMono

### Removed
Expand Down
Binary file modified docs/src/assets/figures/ampersand-matrix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 18 additions & 17 deletions docs/src/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,39 +260,40 @@ end
While drawing, you can copy the data in the form of a
matrix, using the `image_as_matrix()` function.

`image_as_matrix()` returns an `Array{Array{Float64,1},2}`, an array of 4 element arrays that
encode the alpha, Red, Green, and Blue, and Alpha values of each pixel.
`image_as_matrix()` returns a array of ARGB{32} values that encode the Red, Green, and Blue, and Alpha values of each pixel.

The following example draws a white box, then copies the drawing into a matrix called `mat1`. Then it draws the number "42", and copies the updated drawing into `mat2`. Then, the second drawing reads the values in from the two matrices and draws some square tiles depending on the corresponding values in the two matrices ... a very primitive Boolean operation.
The following example draws a red box, then copies the drawing into a matrix called `mat1`. Then it draws a blue circle, and copies the updated drawing into `mat2`. Then, the second drawing reads the values in from the two matrices and draws some square tiles depending on the corresponding values in the two matrices ... a very primitive Boolean operation.

```@example
using Luxor # hide
using Luxor, Colors # hide
Drawing(40, 40, :png)
origin()
background("black")
sethue("white")
fontsize(38)
fontface("Georgia")
box(O, 10, 40, :fill)
mat1 = image_as_matrix()
text("42", halign=:center, valign=:middle)
mat2 = image_as_matrix()
sethue("red")
box(O, 40, 20, :fill)
mat1 = image_as_matrix()'
sethue("blue")
setline(5)
circle(O, 15, :stroke)
mat2 = image_as_matrix()'
finish()
# second drawing
Drawing(400, 400, "assets/figures/image-drawings.svg")
background("darkorange")
background("grey20")
origin()
t = Tiler(400, 400, size(mat1)...)
t = Tiler(400, 400, size(mat1)..., margin=0)
sethue("white")
for (pos, n) in t
u = mat1[t.currentrow, t.currentcol][1:3]/3 +
mat2[t.currentrow, t.currentcol][1:3]/3
if sum(u) < 0.2
pixel1 = convert(Colors.RGBA, mat1[n])
pixel2 = convert(Colors.RGBA, mat2[n])
if red(pixel1) > .5 && blue(pixel2) > .5
randomhue()
box(pos, t.tilewidth - 1, t.tileheight - 1, :fill)
box(pos, t.tilewidth - 1, t.tileheight - 1, :fillstroke)
end
end
finish() # hide
Expand Down
21 changes: 9 additions & 12 deletions docs/src/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,26 @@ You can use an environment such as a Jupyter or Pluto notebook or the Juno or VS

## Images as matrices

With the `@imagematrix` macro, you can create your drawing with vector graphics in the usual way, but the result is returned as a matrix. Each element of the matrix contains four numbers that could define an RGBA pixel.
This example plots the ampersand as if was a matrix of data points.
With the `@imagematrix` macro, you can create your drawing with vector graphics in the usual way, but the result is returned as a matrix. This example processes the ampersand in Images.jl.

```
using Luxor, Colors
using Luxor, Colors, Images, ImageFiltering
m = @imagematrix begin
background("black")
sethue("white")
fontface("Georgia")
fontsize(50)
fontsize(180)
text("&", halign=:center, valign=:middle)
end 60 60
end 200 200
# m is a 60×60 Array{Array{Float64,1},2}:
# [0.0, 0.0, 0.0, 1.0] [0.0, 0.0, 0.0, 1.0] [0.996078, 0.996078, 0.996078, 1.0] ...
# [0.603922, 0.603922, 0.603922, 1.0] [0.0, 0.0, 0.0, 1.0] ...
# [0.815686, 0.815686, 0.815686, 1.0] ...
function convertmatrixtocolors(m)
return convert.(Colors.RGBA, m)
end
# convert to simple black/white array
mg = map(k -> round.(float(Gray(RGBA(k...))), digits=0), m)
img = convertmatrixtocolors(m)
Plots.spy(mg, markersize=3)
imfilter(img, Kernel.gaussian(10))
```

![image matrix](assets/figures/ampersand-matrix.png)
Expand Down
104 changes: 55 additions & 49 deletions src/drawings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -602,22 +602,6 @@ macro draw(body, width=600, height=600)
end
end

# function image_as_matrix()
# if length(CURRENTDRAWING) != 1
# error("no current drawing")
# end
# w = Int(current_surface().width)
# h = Int(current_surface().height)
# imagesurface = CairoImageSurface(fill(ARGB32(1, 1, 1, 0), w, h))
# cr = Cairo.CairoContext(imagesurface)
# Cairo.set_source_surface(cr, current_surface(), 0, 0)
# Cairo.paint(cr)
# data = imagesurface.data
# Cairo.finish(imagesurface)
# Cairo.destroy(imagesurface)
# return reinterpret(ARGB32, permutedims(data, (2, 1)))
# end

"""
_argb32_to_rgba(i)
Expand Down Expand Up @@ -682,16 +666,15 @@ end
"""
image_as_matrix()
If the current Luxor drawing is an `:image` type, return an
Array of the current state of the picture, where each
element is an array of the R, G, B, and A values.
Return an Array of the current state of the picture as an
array of ARGB32.
A matrix 50 wide and 30 high => a table 30 rows by 50 cols
```
using Luxor, Images
Drawing(50, 50, :image)
Drawing(50, 50, :png)
origin()
background(randomhue()...)
sethue("white")
Expand All @@ -700,12 +683,6 @@ fontface("Georgia")
text("42", halign=:center, valign=:middle)
mat = image_as_matrix()
finish()
# working in Images
# convert matrix to RGBA matrix
img = map(k -> RGBA.(k...), mat)
display(imresize(img, 150, 150))
```
"""
function image_as_matrix()
Expand All @@ -715,27 +692,45 @@ function image_as_matrix()
w = Int(current_surface().width)
h = Int(current_surface().height)
z = zeros(UInt32, w, h)

# create a new image surface to receive the data from the current drawing
imagesurface = CairoImageSurface(z, Cairo.FORMAT_ARGB32)

# the destination - we're drawing on this
crdest = Cairo.CairoContext(imagesurface)

# set the source to be the current Luxor drawing
Cairo.set_source_surface(crdest, Luxor.current_surface(), 0, 0)

Cairo.set_operator(crdest, Cairo.OPERATOR_SOURCE)
Cairo.paint(crdest)

cr = Cairo.CairoContext(imagesurface)
Cairo.set_source_surface(cr, current_surface(), 0, 0)
Cairo.paint(cr)
data = imagesurface.data
r = unpremultiplyalpha(data)

Cairo.finish(imagesurface)
Cairo.destroy(imagesurface)
return permutedims(r, (2, 1))
return reinterpret(ARGB32, permutedims(data, (2, 1)))
end

# function image_as_matrix()
# if length(CURRENTDRAWING) != 1
# error("no current drawing")
# end
# w = Int(current_surface().width)
# h = Int(current_surface().height)
# z = zeros(UInt32, w, h)
#
# # create a new image surface to receive the data from the current drawing
# imagesurface = CairoImageSurface(z, Cairo.FORMAT_ARGB32)
#
# # the destination - we're drawing on this
# crdest = Cairo.CairoContext(imagesurface)
#
# # set the source to be the current Luxor drawing
# Cairo.set_source_surface(crdest, Luxor.current_surface(), 0, 0)
#
# Cairo.set_operator(crdest, Cairo.OPERATOR_SOURCE)
# Cairo.paint(crdest)
#
# data = imagesurface.data
# r = unpremultiplyalpha(data)
#
# Cairo.finish(imagesurface)
# Cairo.destroy(imagesurface)
# return permutedims(r, (2, 1))
# end

"""
Create a drawing and return a matrix of the image.
Expand All @@ -753,28 +748,39 @@ m = @imagematrix begin
end 60 60
julia> m[1220:1224] |> show
[[0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 1.0],
[1.0, 0.0, 0.0, 1.0],
[1.0, 0.0, 0.0, 1.0],
[1.0, 0.0, 0.0, 1.0]]
ARGB32[ARGB32(0.0N0f8,0.0N0f8,0.0N0f8,0.0N0f8),
ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8),
ARGB32(1.0N0f8,0.0N0f8,0.0N0f8,1.0N0f8)]
```
If, for some strange reason you want to draw the matrix as another
Luxor drawing again, use code such as this:
```
using Colors
m = @imagematrix begin
sethue("red")
box(O, 20, 20, :fill)
sethue("blue")
box(O, 10, 40, :fill)
end 60 60
function convertmatrixtocolors(m)
return convert.(Colors.RGBA, m)
end
function drawimagematrix(m)
d = Drawing(500, 500, "/tmp/temp.png")
origin()
background("yellow")
w, h = size(m)
t = Tiler(500, 500, w, h)
mi = convertmatrixtocolors(m)
@show mi[30, 30]
for (pos, n) in t
c = m[t.currentrow, t.currentcol]
setcolor(RGBA(c...))
c = mi[t.currentrow, t.currentcol]
setcolor(c)
box(pos, t.tilewidth -1, t.tileheight - 1, :fill)
end
finish()
Expand Down
58 changes: 21 additions & 37 deletions test/imagematrix.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
using Luxor, Test, Colors

function convertmatrixtocolors(m)
return @. convert(Colors.RGBA{Float64}, m)
end

function imagematrix()
Drawing(2, 2, :png)

# get image as matrix
mat = image_as_matrix()

@test mat[1] == [0.0, 0.0, 0.0, 0.0]
mat = convertmatrixtocolors(image_as_matrix())

@test mat[1] == RGBA{Float64}(0.0, 0.0, 0.0, 0.0)

@test mat[1] == mat[2] == mat[3] == mat[4]

# make it red, then get image as matrix again
Expand All @@ -15,7 +21,7 @@ function imagematrix()
paint()
mat = image_as_matrix()

@test mat[1] == [1.0, 0.0, 0.0, 1.0]
@test mat[1] == RGBA{Float64}(1.0, 0.0, 0.0, 1.0)
@test mat[1] == mat[2] == mat[3] == mat[4]


Expand All @@ -24,18 +30,20 @@ function imagematrix()
paint()

mat = image_as_matrix()
@test mat[1] == [0.0, 1.0, 0.0, 1.0]
@test mat[1] == RGBA{Float64}(0.0, 1.0, 0.0, 1.0)
@test mat[1] == mat[2] == mat[3] == mat[4]

finish()

# test alpha
Drawing(2, 2, :png)

setcolor(0, 0, 0, .5)
paint()
mat = image_as_matrix()
@test mat[1][1] == 0.0
@test round(mat[2][2], digits=1) 0.5
@test mat[3][3] == 0.0
@test mat[4][4] == 1.0

@test red(mat[1]) == 0.0
@test isapprox(alpha(mat[1]), 0.5, atol=0.1)

@test finish() == true

Expand All @@ -49,42 +57,18 @@ function imagematrix()
for (pos, n) in tiles
isodd(n) && box(pos, 1, 1, :fill)
end
mat = image_as_matrix()
mat = convertmatrixtocolors(image_as_matrix())

# first square is blue
@test mat[1][1] == 0.0
@test mat[1][2] == 0.0
@test mat[1][3] == 1.0
@test mat[1][4] == 1.0
@test blue(mat[1]) == 1.0

# second square is white
@test mat[2][1] == 1.0
@test mat[2][2] == 1.0
@test mat[2][3] == 1.0
@test mat[2][4] == 1.0
@test red(mat[2]) == blue(mat[2]) == green(mat[2])

# third square is blue
@test mat[3][1] == 0.0
@test mat[3][2] == 0.0
@test mat[3][3] == 1.0
@test mat[3][4] == 1.0

m = @imagematrix begin
background(1, 0.5, 0.5, 0.5)
end 5 5

# test that rounding errors for alpha aren't too bad
@test isapprox(m[1][1], 1.0, atol=0.01)
@test isapprox(m[1][2], 0.5, atol=0.01)
@test isapprox(m[1][3], 0.5, atol=0.01)
@test isapprox(m[1][4], 0.5, atol=0.01)

# test each element is the same "color"
@test all(x -> isequal(x, 1), [el[1] for el in m])
@test all(x -> isapprox(x, 0.5, atol=0.01), [el[2] for el in m])
@test all(x -> isapprox(x, 0.5, atol=0.01), [el[3] for el in m])
@test all(x -> isapprox(x, 0.5, atol=0.01), [el[4] for el in m])
@test blue(mat[2]) == 1.0

@test finish() == true
end

imagematrix()
Expand Down

0 comments on commit f14f9da

Please sign in to comment.