@@ -49,7 +49,7 @@ proc alphaBleed*(image: Image) =
4949 var
5050 layers: seq [Image ]
5151 min = image.minifyBy2Alpha ()
52- while min.width >= 1 and min.height >= 1 :
52+ while min.width >= 2 and min.height >= 2 :
5353 layers.add min
5454 min = min.minifyBy2Alpha ()
5555
@@ -81,7 +81,7 @@ proc save*(flippy: Flippy, filePath: string) =
8181 # TODO Talk to Ryan about format data compression.
8282 var s = newStringStream ()
8383 for c in mip.data:
84- s.write (c)
84+ s.write (c. toStraightAlpha () )
8585 s.setPosition (0 )
8686 var stringData = s.readAll ()
8787 var zipped = compress (stringData)
@@ -133,246 +133,15 @@ proc loadFlippy*(filePath: string): Flippy =
133133 # TODO : Talk to ryan about compression
134134 var unzipped = uncompress (zipped)
135135 var s = newStringStream (cast [string ](unzipped))
136- mip.data = newSeq [ColorRGBA ](mip.width * mip.height)
136+ mip.data = newSeq [ColorRGBX ](mip.width * mip.height)
137137 for c in mip.data.mitems:
138- s.read (c)
138+ var rgba: ColorRGBA
139+ s.read (rgba)
140+ c = rgba
139141 # mip.data = uncompress(zipped)
140142
141143 result .mipmaps.add (mip)
142144
143- proc fill2 * (image: Image , rgba: ColorRGBA ) =
144- # # Fills the image with a solid color.
145- var i = 0
146- while i < image.data.len:
147- cast [ptr uint32 ](image.data [i + 0 ].addr )[] = cast [uint32 ](rgba)
148- # This accomplishes the same thing as:
149- # image.data[i + 0] = rgba.r
150- # image.data[i + 1] = rgba.g
151- # image.data[i + 2] = rgba.b
152- # image.data[i + 3] = rgba.a
153- i += 1
154-
155- proc blitUnsafe * (destImage: Image , srcImage: Image , src, dest: Rect ) =
156- # # Blits rectangle from one image to the other image.
157- # # * No bounds checking *
158- # # Make sure that src and dest rect are in bounds.
159- # # Make sure that channels for images are the same.
160- # # Failure in the assumptions will case unsafe memory writes.
161- # # Note: Does not do alpha or color mixing.
162- let c = 4
163- for y in 0 ..< int (dest.h):
164- let
165- srcIdx = int (src.x) + (int (src.y) + y) * srcImage.width
166- destIdx = int (dest.x) + (int (dest.y) + y) * destImage.width
167- copyMem (
168- destImage.data[destIdx].addr ,
169- srcImage.data[srcIdx].addr ,
170- int (dest.w) * c
171- )
172-
173- proc blit * (destImage: Image , srcImage: Image , src, dest: Rect ) =
174- # # Blits rectangle from one image to the other image.
175- # # Note: Does not do alpha or color mixing.
176- doAssert src.w == dest.w and src.h == dest.h
177- doAssert src.x >= 0 and src.x + src.w <= srcImage.width.float32
178- doAssert src.y >= 0 and src.y + src.h <= srcImage.height.float32
179-
180- # See if the image hits the bounds and needs to be adjusted.
181- var
182- src = src
183- dest = dest
184- if dest.x < 0 :
185- dest.w += dest.x
186- src.x -= dest.x
187- src.w += dest.x
188- dest.x = 0
189- if dest.x + dest.w > destImage.width.float32 :
190- let diff = destImage.width.float32 - (dest.x + dest.w)
191- dest.w += diff
192- src.w += diff
193- if dest.y < 0 :
194- dest.h += dest.y
195- src.y -= dest.y
196- src.h += dest.y
197- dest.y = 0
198- if dest.y + dest.h > destImage.height.float32 :
199- let diff = destImage.height.float32 - (dest.y + dest.h)
200- dest.h += diff
201- src.h += diff
202-
203- # See if image is entirely outside the bounds:
204- if dest.x + dest.w < 0 or dest.x > destImage.width.float32 :
205- return
206- if dest.y + dest.h < 0 or dest.y > destImage.height.float32 :
207- return
208-
209- # Faster path using copyMem:
210- blitUnsafe (destImage, srcImage, src, dest)
211-
212- proc line * (image: Image , at, to: Vec2 , rgba: ColorRGBA ) =
213- # # Draws a line from one at vec to to vec.
214- let
215- dx = to.x - at.x
216- dy = to.y - at.y
217- var x = at.x
218- while true :
219- if dx == 0 :
220- break
221- let y = at.y + dy * (x - at.x) / dx
222- image[int x, int y] = rgba
223- if at.x < to.x:
224- x += 1
225- if x > to.x:
226- break
227- else :
228- x -= 1
229- if x < to.x:
230- break
231-
232- var y = at.y
233- while true :
234- if dy == 0 :
235- break
236- let x = at.x + dx * (y - at.y) / dy
237- image[int x, int y] = rgba
238- if at.y < to.y:
239- y += 1
240- if y > to.y:
241- break
242- else :
243- y -= 1
244- if y < to.y:
245- break
246-
247- proc fillRect * (image: Image , rect: Rect , rgba: ColorRGBA ) =
248- # # Draws a filled rectangle.
249- let
250- minX = max (int (rect.x), 0 )
251- maxX = min (int (rect.x + rect.w), image.width)
252- minY = max (int (rect.y), 0 )
253- maxY = min (int (rect.y + rect.h), image.height)
254- for y in minY ..< maxY:
255- for x in minX ..< maxX:
256- image.setRgbaUnsafe (x, y, rgba)
257-
258- proc strokeRect * (image: Image , rect: Rect , rgba: ColorRGBA ) =
259- # # Draws a rectangle borders only.
260- let
261- at = rect.xy
262- wh = rect.wh - vec2 (1 , 1 ) # line width
263- image.line (at, at + vec2 (wh.x, 0 ), rgba)
264- image.line (at + vec2 (wh.x, 0 ), at + vec2 (wh.x, wh.y), rgba)
265- image.line (at + vec2 (0 , wh.y), at + vec2 (wh.x, wh.y), rgba)
266- image.line (at + vec2 (0 , wh.y), at, rgba)
267-
268- proc blit * (destImage: Image , srcImage: Image , pos: Vec2 ) =
269- # # Blits rectangle from one image to the other image.
270- # # Note: Does not do alpha or color mixing.
271- destImage.blit (
272- srcImage,
273- rect (0.0 , 0.0 , srcImage.width.float32 , srcImage.height.float32 ),
274- rect (pos.x, pos.y, srcImage.width.float32 , srcImage.height.float32 )
275- )
276-
277- proc fillCircle * (image: Image , pos: Vec2 , radius: float , rgba: ColorRGBA ) =
278- # # Draws a filled circle with antialiased edges.
279- let
280- minX = max (int (pos.x - radius), 0 )
281- maxX = min (int (pos.x + radius), image.width)
282- minY = max (int (pos.y - radius), 0 )
283- maxY = min (int (pos.y + radius), image.height)
284- for x in minX ..< maxX:
285- for y in minY ..< maxY:
286- let
287- pixelPos = vec2 (float x, float y) + vec2 (0.5 , 0.5 )
288- pixelDist = pixelPos.dist (pos)
289- if pixelDist < radius - sqrt (0.5 ):
290- image.setRgbaUnsafe (x, y, rgba)
291- elif pixelDist < radius + sqrt (0.5 ):
292- var touch = 0
293- const n = 5
294- const r = (n - 1 ) div 2
295- for aay in - r .. r:
296- for aax in - r .. r:
297- if pos.dist (pixelPos + vec2 (aay / n, aax / n)) < radius:
298- inc touch
299- var rgbaAA = rgba
300- rgbaAA.a = uint8 (float (touch) * 255.0 / (n * n))
301- image.setRgbaUnsafe (x, y, rgbaAA)
302-
303- proc strokeCircle * (
304- image: Image , pos: Vec2 , radius, border: float , rgba: ColorRGBA
305- ) =
306- # # Draws a border of circle with antialiased edges.
307- let
308- minX = max (int (pos.x - radius - border), 0 )
309- maxX = min (int (pos.x + radius + border), image.width)
310- minY = max (int (pos.y - radius - border), 0 )
311- maxY = min (int (pos.y + radius + border), image.height)
312- for y in minY ..< maxY:
313- for x in minX ..< maxX:
314- let
315- pixelPos = vec2 (float x, float y) + vec2 (0.5 , 0.5 )
316- pixelDist = pixelPos.dist (pos)
317- if pixelDist > radius - border / 2 - sqrt (0.5 ) and
318- pixelDist < radius + border / 2 + sqrt (0.5 ):
319- var touch = 0
320- const
321- n = 5
322- r = (n - 1 ) div 2
323- for aay in - r .. r:
324- for aax in - r .. r:
325- let dist = pos.dist (pixelPos + vec2 (aay / n, aax / n))
326- if dist > radius - border/ 2 and dist < radius + border/ 2 :
327- inc touch
328- var rgbaAA = rgba
329- rgbaAA.a = uint8 (float (touch) * 255.0 / (n * n))
330- image.setRgbaUnsafe (x, y, rgbaAA)
331-
332- proc fillRoundedRect * (
333- image: Image , rect: Rect , radius: float , rgba: ColorRGBA
334- ) =
335- # # Fills image with a rounded rectangle.
336- image.fill2 (rgba)
337- let
338- borderWidth = radius
339- borderWidthPx = int ceil (radius)
340- var corner = newImage (borderWidthPx, borderWidthPx)
341- corner.fillCircle (vec2 (borderWidth, borderWidth), radius, rgba)
342- image.blit (corner, vec2 (0 , 0 ))
343- corner.flipHorizontal ()
344- image.blit (corner, vec2 (rect.w - borderWidth, 0 )) # NE
345- corner.flipVertical ()
346- image.blit (corner, vec2 (rect.w - borderWidth, rect.h - borderWidth)) # SE
347- corner.flipHorizontal ()
348- image.blit (corner, vec2 (0 , rect.h - borderWidth)) # SW
349-
350- proc strokeRoundedRect * (
351- image: Image , rect: Rect , radius, border: float , rgba: ColorRGBA
352- ) =
353- # # Fills image with a stroked rounded rectangle.
354- # var radius = min(radius, rect.w/2)
355- for i in 0 ..< int (border):
356- let f = float i
357- image.strokeRect (rect (
358- rect.x + f,
359- rect.y + f,
360- rect.w - f * 2 ,
361- rect.h - f * 2 ,
362- ), rgba)
363- let borderWidth = (radius + border / 2 )
364- let borderWidthPx = int ceil (borderWidth)
365- var corner = newImage (borderWidthPx, borderWidthPx)
366- corner.strokeCircle (vec2 (borderWidth, borderWidth), radius, border, rgba)
367- let s = borderWidth.ceil
368- image.blit (corner, vec2 (0 , 0 )) # NW
369- corner.flipHorizontal ()
370- image.blit (corner, vec2 (rect.w - s, 0 )) # NE
371- corner.flipVertical ()
372- image.blit (corner, vec2 (rect.w - s, rect.h - s)) # SE
373- corner.flipHorizontal ()
374- image.blit (corner, vec2 (0 , rect.h - s)) # SW
375-
376145proc outlineBorder * (image: Image , borderPx: int ): Image =
377146 # # Adds n pixel border around alpha parts of the image.
378147 result = newImage (
0 commit comments