@@ -49,7 +49,7 @@ proc alphaBleed*(image: Image) =
49
49
var
50
50
layers: seq [Image]
51
51
min = image.minifyBy2Alpha()
52
- while min.width >= 1 and min.height >= 1 :
52
+ while min.width >= 2 and min.height >= 2 :
53
53
layers.add min
54
54
min = min.minifyBy2Alpha()
55
55
@@ -81,7 +81,7 @@ proc save*(flippy: Flippy, filePath: string) =
81
81
# TODO Talk to Ryan about format data compression.
82
82
var s = newStringStream()
83
83
for c in mip.data:
84
- s.write(c)
84
+ s.write(c.toStraightAlpha() )
85
85
s.setPosition(0 )
86
86
var stringData = s.readAll()
87
87
var zipped = compress(stringData)
@@ -133,246 +133,15 @@ proc loadFlippy*(filePath: string): Flippy =
133
133
# TODO : Talk to ryan about compression
134
134
var unzipped = uncompress(zipped)
135
135
var s = newStringStream(cast [string ](unzipped))
136
- mip.data = newSeq[ColorRGB A ](mip.width * mip.height)
136
+ mip.data = newSeq[ColorRGB X ](mip.width * mip.height)
137
137
for c in mip.data.mitems:
138
- s.read(c)
138
+ var rgba: ColorRGBA
139
+ s.read(rgba)
140
+ c = rgba
139
141
#mip.data = uncompress(zipped)
140
142
141
143
result .mipmaps.add(mip)
142
144
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
-
376
145
proc outlineBorder*(image: Image, borderPx: int ): Image =
377
146
## Adds n pixel border around alpha parts of the image.
378
147
result = newImage(
0 commit comments