Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(292)

Delta Between Two Patch Sets: draw/scale.go

Issue 101670045: code review 101670045: go.image/draw: new package, a superset of the standard ...
Left Patch Set: diff -r e492ffa4cad4 https://code.google.com/p/go.image Created 9 years, 9 months ago
Right Patch Set: diff -r e492ffa4cad4 https://code.google.com/p/go.image Created 9 years, 8 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « draw/draw.go ('k') | draw/scale_test.go » ('j') | draw/scale_test.go » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
1 package draw 5 package draw
chai2010 2014/07/04 10:50:32 copyright missing
nigeltao 2014/07/07 00:58:40 Done.
2 6
3 import ( 7 import (
4 "image" 8 "image"
5 "image/color" 9 "image/color"
6 "math" 10 "math"
7 ) 11 )
8 12
9 // Quality is a desired resampling quality, ranging from the fastest 13 // Quality is a desired resampling quality, ranging from the fastest
10 // computation (lowest quality) to the slowest computation (highest quality). 14 // computation (lowest quality) to the slowest computation (highest quality).
11 type Quality int 15 type Quality int
12 16
13 const ( 17 const (
14 FastestQuality Quality = -1 18 FastestQuality Quality = -1
15 DefaultQuality Quality = 0 19 DefaultQuality Quality = 0
16 NicestQuality Quality = +1 20 NicestQuality Quality = +1
r 2014/07/08 19:44:54 not sure i like these names. they don't convey rea
17 ) 21 )
18 22
19 // Scale scales the part of the source image defined by src and sr and writes 23 // Scale scales the part of the source image defined by src and sr and writes
20 // to the part of the destination image defined by dst and dr. 24 // to the part of the destination image defined by dst and dr.
21 func Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, q Quality) { 25 func Scale(dst Image, dr image.Rectangle, src image.Image, sr image.Rectangle, q Quality) {
22 NewScaler(dr.Size(), sr.Size(), q).Scale(dst, dr.Min, src, sr.Min) 26 NewScaler(dr.Size(), sr.Size(), q).Scale(dst, dr.Min, src, sr.Min)
23 } 27 }
24 28
25 // Scaler scales part of a source image, starting from sp, and writes to a 29 // Scaler scales part of a source image, starting from sp, and writes to a
26 // destination image, starting from dp. The destination and source width and 30 // destination image, starting from dp. The destination and source width and
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 dst.Set(dp.X+int(dx), dp.Y+int(dy), dstColor) 97 dst.Set(dp.X+int(dx), dp.Y+int(dy), dstColor)
94 } 98 }
95 } 99 }
96 } 100 }
97 101
98 // defaultScaler implements a bi-linear image scaler. 102 // defaultScaler implements a bi-linear image scaler.
99 type defaultScaler struct { 103 type defaultScaler struct {
100 dw, dh, sw, sh int32 104 dw, dh, sw, sh int32
101 } 105 }
102 106
103 func (z *defaultScaler) Scale(dst Image, dp image.Point, src image.Image, sp ima ge.Point) { 107 func (z *defaultScaler) Scale(dst Image, dp image.Point, src image.Image, sp ima ge.Point) {
klauspost 2014/07/06 21:05:11 If I read this algorithm correctly, it will be bro
nigeltao 2014/07/07 00:58:40 I don't think you're reading it correctly. This lo
klauspost 2014/07/17 09:55:17 Ok, I am sorry to say that I am reading it correct
104 yscale := float64(z.sh) / float64(z.dh) 108 yscale := float64(z.sh) / float64(z.dh)
105 xscale := float64(z.sw) / float64(z.dw) 109 xscale := float64(z.sw) / float64(z.dw)
106 dstColorRGBA64 := &color.RGBA64{} 110 dstColorRGBA64 := &color.RGBA64{}
107 dstColor := color.Color(dstColorRGBA64) 111 dstColor := color.Color(dstColorRGBA64)
108 for dy := int32(0); dy < z.dh; dy++ { 112 for dy := int32(0); dy < z.dh; dy++ {
109 sy := (float64(dy)+0.5)*yscale - 0.5 113 sy := (float64(dy)+0.5)*yscale - 0.5
110 sy0 := int32(sy) 114 sy0 := int32(sy)
111 yFrac0 := sy - float64(sy0) 115 yFrac0 := sy - float64(sy0)
112 yFrac1 := 1 - yFrac0 116 yFrac1 := 1 - yFrac0
113 sy1 := sy0 + 1 117 sy1 := sy0 + 1
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 horizontal, vertical distrib 182 horizontal, vertical distrib
179 } 183 }
180 184
181 func (z *nicestScaler) Scale(dst Image, dp image.Point, src image.Image, sp imag e.Point) { 185 func (z *nicestScaler) Scale(dst Image, dp image.Point, src image.Image, sp imag e.Point) {
182 // TODO: is it worth having a sync.Pool for this temporary buffer? 186 // TODO: is it worth having a sync.Pool for this temporary buffer?
183 tmp := make([][4]float64, z.dw*z.sh) 187 tmp := make([][4]float64, z.dw*z.sh)
184 z.scaleX(tmp, src, sp) 188 z.scaleX(tmp, src, sp)
185 z.scaleY(dst, dp, tmp) 189 z.scaleY(dst, dp, tmp)
186 } 190 }
187 191
188 // source is a range of contribs, and their total weight. 192 // source is a range of contribs, their inverse total weight, and that ITW
193 // divided by 0xffff.
189 type source struct { 194 type source struct {
190 » i, j int32 195 » i, j int32
191 » totalWeight float64 196 » invTotalWeight float64
197 » invTotalWeightFFFF float64
192 } 198 }
193 199
194 // contrib is the weight of a column or row. 200 // contrib is the weight of a column or row.
195 type contrib struct { 201 type contrib struct {
196 coord int32 202 coord int32
197 weight float64 203 weight float64
198 } 204 }
199 205
200 // distrib measures how source pixels are distributed over destination pixels. 206 // distrib measures how source pixels are distributed over destination pixels.
201 type distrib struct { 207 type distrib struct {
202 // sources are what contribs each column or row in the source image owns , 208 // sources are what contribs each column or row in the source image owns ,
203 // and the total weight of those contribs. 209 // and the total weight of those contribs.
204 sources []source 210 sources []source
205 // contribs are the contributions indexed by sources[s].i and sources[s] .j. 211 // contribs are the contributions indexed by sources[s].i and sources[s] .j.
206 contribs []contrib 212 contribs []contrib
207 } 213 }
208 214
209 // newDistrib returns a distrib that distributes sw source columns (or rows) 215 // newDistrib returns a distrib that distributes sw source columns (or rows)
210 // over dw destination columns (or rows). 216 // over dw destination columns (or rows).
211 func newDistrib(dw, sw int32) distrib { 217 func newDistrib(dw, sw int32) distrib {
212 scale := float64(sw) / float64(dw) 218 scale := float64(sw) / float64(dw)
213 halfWidth, kernelArgScale := float64(catmullRomSupport), 1.0 219 halfWidth, kernelArgScale := float64(catmullRomSupport), 1.0
214 if scale > 1 { 220 if scale > 1 {
215 halfWidth *= scale 221 halfWidth *= scale
216 kernelArgScale = 1 / scale 222 kernelArgScale = 1 / scale
217 } 223 }
218 224
219 // Make the sources slice, one source for each column or row, and tempor arily 225 // Make the sources slice, one source for each column or row, and tempor arily
220 » // appropriate its elements' fields so that totalWeight is the scaled 226 » // appropriate its elements' fields so that invTotalWeight is the scaled
221 // co-ordinate of the source column or row, and i and j are the lower an d 227 // co-ordinate of the source column or row, and i and j are the lower an d
222 // upper bounds of the range of destination columns or rows affected by the 228 // upper bounds of the range of destination columns or rows affected by the
223 // source column or row. 229 // source column or row.
224 n, sources := int32(0), make([]source, dw) 230 n, sources := int32(0), make([]source, dw)
225 for x := range sources { 231 for x := range sources {
226 center := (float64(x)+0.5)*scale - 0.5 232 center := (float64(x)+0.5)*scale - 0.5
227 i := int32(math.Floor(center - halfWidth)) 233 i := int32(math.Floor(center - halfWidth))
228 if i < 0 { 234 if i < 0 {
229 i = 0 235 i = 0
230 } 236 }
231 j := int32(math.Ceil(center + halfWidth)) 237 j := int32(math.Ceil(center + halfWidth))
232 if j >= sw { 238 if j >= sw {
233 j = sw - 1 239 j = sw - 1
234 if j < i { 240 if j < i {
235 j = i 241 j = i
236 } 242 }
237 } 243 }
238 » » sources[x] = source{i: i, j: j, totalWeight: center} 244 » » sources[x] = source{i: i, j: j, invTotalWeight: center}
239 n += j - i + 1 245 n += j - i + 1
240 } 246 }
241 247
242 contribs := make([]contrib, 0, n) 248 contribs := make([]contrib, 0, n)
243 for k, b := range sources { 249 for k, b := range sources {
244 totalWeight := 0.0 250 totalWeight := 0.0
245 l := int32(len(contribs)) 251 l := int32(len(contribs))
246 for coord := b.i; coord <= b.j; coord++ { 252 for coord := b.i; coord <= b.j; coord++ {
247 » » » weight := catmullRom((b.totalWeight - float64(coord)) * kernelArgScale) 253 » » » weight := catmullRom((b.invTotalWeight - float64(coord)) * kernelArgScale)
248 if weight == 0 { 254 if weight == 0 {
249 continue 255 continue
250 } 256 }
251 totalWeight += weight 257 totalWeight += weight
252 contribs = append(contribs, contrib{coord, weight}) 258 contribs = append(contribs, contrib{coord, weight})
253 } 259 }
260 totalWeight = 1 / totalWeight
254 sources[k] = source{ 261 sources[k] = source{
255 » » » i: l, 262 » » » i: l,
256 » » » j: int32(len(contribs)), 263 » » » j: int32(len(contribs)),
257 » » » totalWeight: totalWeight, 264 » » » invTotalWeight: totalWeight,
klauspost 2014/07/06 21:05:11 Check if totalWeight is zero - otherwise you risk
nigeltao 2014/07/07 00:58:40 And if totalWeight is zero, what do you think shou
265 » » » invTotalWeightFFFF: totalWeight / 0xffff,
258 } 266 }
259 } 267 }
260 268
261 return distrib{sources, contribs} 269 return distrib{sources, contribs}
262 } 270 }
263 271
264 // scaleX distributes the source image's columns over the temporary image. 272 // scaleX distributes the source image's columns over the temporary image.
265 func (z *nicestScaler) scaleX(tmp [][4]float64, src image.Image, sp image.Point) { 273 func (z *nicestScaler) scaleX(tmp [][4]float64, src image.Image, sp image.Point) {
266 t := 0 274 t := 0
267 for y := int32(0); y < z.sh; y++ { 275 for y := int32(0); y < z.sh; y++ {
268 for _, s := range z.horizontal.sources { 276 for _, s := range z.horizontal.sources {
269 var r, g, b, a float64 277 var r, g, b, a float64
270 for _, c := range z.horizontal.contribs[s.i:s.j] { 278 for _, c := range z.horizontal.contribs[s.i:s.j] {
271 rr, gg, bb, aa := src.At(sp.X+int(c.coord), sp.Y +int(y)).RGBA() 279 rr, gg, bb, aa := src.At(sp.X+int(c.coord), sp.Y +int(y)).RGBA()
272 r += float64(rr) * c.weight 280 r += float64(rr) * c.weight
273 g += float64(gg) * c.weight 281 g += float64(gg) * c.weight
274 b += float64(bb) * c.weight 282 b += float64(bb) * c.weight
275 a += float64(aa) * c.weight 283 a += float64(aa) * c.weight
276 } 284 }
277 » » » tw := s.totalWeight * 0xffff 285 » » » tmp[t] = [4]float64{
klauspost 2014/07/06 21:05:11 Precompute this value and store the inverse. That
nigeltao 2014/07/07 00:58:40 Done.
278 » » » tmp[t] = [4]float64{r / tw, g / tw, b / tw, a / tw} 286 » » » » r * s.invTotalWeightFFFF,
287 » » » » g * s.invTotalWeightFFFF,
288 » » » » b * s.invTotalWeightFFFF,
289 » » » » a * s.invTotalWeightFFFF,
290 » » » }
279 t++ 291 t++
280 } 292 }
281 } 293 }
282 } 294 }
283 295
284 // scaleY distributes the temporary image's rows over the destination image. 296 // scaleY distributes the temporary image's rows over the destination image.
285 func (z *nicestScaler) scaleY(dst Image, dp image.Point, tmp [][4]float64) { 297 func (z *nicestScaler) scaleY(dst Image, dp image.Point, tmp [][4]float64) {
286 dstColorRGBA64 := &color.RGBA64{} 298 dstColorRGBA64 := &color.RGBA64{}
287 dstColor := color.Color(dstColorRGBA64) 299 dstColor := color.Color(dstColorRGBA64)
288 for x := int32(0); x < z.dw; x++ { 300 for x := int32(0); x < z.dw; x++ {
289 for y, s := range z.vertical.sources { 301 for y, s := range z.vertical.sources {
290 var r, g, b, a float64 302 var r, g, b, a float64
291 for _, c := range z.vertical.contribs[s.i:s.j] { 303 for _, c := range z.vertical.contribs[s.i:s.j] {
292 p := &tmp[c.coord*z.dw+x] 304 p := &tmp[c.coord*z.dw+x]
293 r += p[0] * c.weight 305 r += p[0] * c.weight
294 g += p[1] * c.weight 306 g += p[1] * c.weight
295 b += p[2] * c.weight 307 b += p[2] * c.weight
296 a += p[3] * c.weight 308 a += p[3] * c.weight
297 } 309 }
298 » » » dstColorRGBA64.R = ftou(r / s.totalWeight) 310 » » » dstColorRGBA64.R = ftou(r * s.invTotalWeight)
klauspost 2014/07/06 21:05:11 You should precompute 1.0/s.TotalWeight (and store
nigeltao 2014/07/07 00:58:40 Done.
299 » » » dstColorRGBA64.G = ftou(g / s.totalWeight) 311 » » » dstColorRGBA64.G = ftou(g * s.invTotalWeight)
300 » » » dstColorRGBA64.B = ftou(b / s.totalWeight) 312 » » » dstColorRGBA64.B = ftou(b * s.invTotalWeight)
301 » » » dstColorRGBA64.A = ftou(a / s.totalWeight) 313 » » » dstColorRGBA64.A = ftou(a * s.invTotalWeight)
302 dst.Set(dp.X+int(x), dp.Y+y, dstColor) 314 dst.Set(dp.X+int(x), dp.Y+y, dstColor)
303 } 315 }
304 } 316 }
305 } 317 }
306 318
307 // catmullRom is the Catmull-Rom kernel. 319 // catmullRom is the Catmull-Rom kernel.
308 // 320 //
309 // It is an instance of the more general cubic BC-spline kernel with parameters 321 // It is an instance of the more general cubic BC-spline kernel with parameters
310 // B=0 and C=0.5. See Mitchell and Netravali, "Reconstruction Filters in 322 // B=0 and C=0.5. See Mitchell and Netravali, "Reconstruction Filters in
311 // Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228. 323 // Computer Graphics", Computer Graphics, Vol. 22, No. 4, pp. 221-228.
(...skipping 18 matching lines...) Expand all
330 342
331 func ftou(f float64) uint16 { 343 func ftou(f float64) uint16 {
332 i := int32(0xffff*f + 0.5) 344 i := int32(0xffff*f + 0.5)
333 if i > 0xffff { 345 if i > 0xffff {
334 return 0xffff 346 return 0xffff
335 } else if i > 0 { 347 } else if i > 0 {
336 return uint16(i) 348 return uint16(i)
337 } 349 }
338 return 0 350 return 0
339 } 351 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b