aboutsummaryrefslogtreecommitdiff
path: root/sys/plio/plupdate.x
blob: bb90cc34cc3379003cffbb2329a2f0ad1c609a83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.

include	<syserr.h>
include	<mach.h>
include <plset.h>
include	<plio.h>

# PL_UPDATE -- Update the indicated line of a mask, i.e., insert a possibly
# modified line (encoded line list) into an image mask.  This is where image
# compression in the Y direction occurs: if the line to be inserted is the
# "empty line", or a copy of the adjacent line earlier in the image, then we
# merely set the new line pointer to point to the line already stored, and
# increment the reference count for that line.  When a new mask is created,
# all lines point to the "empty line" line list (PL_EMPTYLINE), which has a
# reference count equal to the number of lines in the mask.
#
# A new line may replace an existing line in storage if space permits and the
# reference count of the existing line is one or less, i.e., the stored line
# list is used only by the image line being updated.  Otherwise the new line
# list will be appended to the line list buffer, which is resized to a larger
# size if it overflows.  Note that the resize may move the buffer, which is
# why all line "pointers" are actually offsets into the line list buffer.
#
# We do not perform garbage collection on freed lines, rather we keep a count
# of the total amount of freed space (which cannot be reused), allowing the
# entire line list to be rebuilt periodically to reclaim the space, when the
# percentage of wasted space rises to a certain level.  Since this can be a
# relatively expensive operation it is only performed by high level, more
# macroscopic operators, e.g., following a full mask rasterop, or before
# writing the mask to external storage.  Note also that rebulding the mask
# may shift the stored line lists about, invalidating any line list pointers
# that may be cached in the calling procedure.  This is another reason why
# we do not perform mask compression automatically if buffer overflow occurs
# during a line update.

procedure pl_update (pl, v, ll)

pointer	pl			#I mask descriptor
long	v[PL_MAXDIM]		#I coordinates of line in the mask
short	ll[ARB]			#I encoded line list

pointer	o_pp, n_pp
int	totlen, axlen, index, i
int	o_lp, n_lp, o_len, n_len, b_len

bool	pll_equal()
int	pl_alloc()
errchk	pl_alloc
define	update_ 91
define	oob_ 92

begin
	# Compute the index of the line in the line pointer array.
	if (PL_NAXES(pl) == 2) {
	    # Optimized for case naxes=2.
	    index = v[2]
	    if (index < 1 || index > PL_AXLEN(pl,2))
		goto oob_
	} else {
	    # Generalized code.
	    index = 1
	    totlen = 1
	    do i = 2, PL_NAXES(pl) {
		axlen = PL_AXLEN(pl,i)
		if (v[i] < 1 || v[i] > axlen)
		    goto oob_
		index = index + totlen * (v[i] - 1)
		totlen = totlen * axlen
	    }
	}

	# Now the pointer to the current stored line list.
	o_lp = PL_LP(pl,index)
	o_pp = Ref (pl, o_lp)
	if (o_pp == NULL)
	    return

	# Has the line been modified?
	n_len = LL_LEN(ll)
	if (n_len == LP_LEN(o_pp))
	    if (pll_equal (ll, LL(pl,o_lp)))
		return

	# Keep track of the number of edits.
	PL_LLNUPDATES(pl) = PL_LLNUPDATES(pl) + 1

	# Is the new line empty?
	n_lp = PL_EMPTYLINE
	n_pp = Ref (pl, n_lp)
	if (n_len == LP_LEN(n_pp))
	    if (pll_equal (ll, LL(pl,n_lp)))
		goto update_

	# Is the new line a copy of the adjacent line (Y=Y-1) in the image?
	# Due to the short integer encoding a maximum of MAX_SHORT references
	# are allowed per line.

	if (index > 1) {
	    n_lp = PL_LP(pl,index-1)
	    n_pp = Ref (pl, n_lp)
	    if (LP_NREF(n_pp) < MAX_SHORT)
		if (n_len == LP_LEN(n_pp))
		    if (pll_equal (ll, LL(pl,n_lp)))
			goto update_
	}

	# The new line isn't a copy of the empty line or of an adjacent line,
	# so copy it into the line list buffer.

	b_len = LP_BLEN(o_pp)
	if (LP_NREF(o_pp) <= 1 && n_len <= b_len && o_lp != PL_EMPTYLINE) {
	    # Overwrite existing line.

	    o_len = LP_LEN(o_pp)
	    call amovs (ll, LL(pl,o_lp), n_len)
	    LP_NREFS(o_pp) = 1
	    LP_SETBLEN(o_pp, b_len)
	    PL_LLFREE(pl) = PL_LLFREE(pl) + (o_len - LP_LEN(o_pp))
	    return

	} else {
	    # Add a new line.

	    n_lp = pl_alloc (pl, n_len)
	    o_pp = Ref (pl, o_lp)
	    n_pp = Ref (pl, n_lp)
	    call amovs (ll, LL(pl,n_lp), n_len)

	    LP_NREFS(n_pp) = 0
	    LP_SETBLEN(n_pp, n_len)
	    PL_LLFREE(pl) = PL_LLFREE(pl) + n_len
	}

	# We get here only if the new line has already been stored in the
	# linelist buffer, with a pointer to such in N_LP, and a pointer to
	# the old line in O_LP.  Dereference the old line, which the new line
	# is no longer associated with, and increase the reference count of
	# the new line.
update_

	# Dereference the old line and free the space if it is no longer used.
	# If the old line buffer is freed we reclaim only LP_LEN words, since
	# we already reclaimed LP_BLEN-LP_LEN in an earlier edit operation.

	LP_NREFS(o_pp) = LP_NREFS(o_pp) - 1
	if (LP_NREF(o_pp) == 0 && o_lp != PL_EMPTYLINE)
	    PL_LLFREE(pl) = PL_LLFREE(pl) + LP_LEN(o_pp)

	# Add another reference to the new line.
	LP_NREFS(n_pp) = LP_NREFS(n_pp) + 1
	if (LP_NREF(n_pp) == 1 && n_lp != PL_EMPTYLINE)
	    PL_LLFREE(pl) = PL_LLFREE(pl) - LP_BLEN(n_pp)

	PL_LP(pl,index) = n_lp
	return
oob_
	call syserr (SYS_PLREFOOB)
end