Odin SolutionsOdin Solutions
⭐
Counting Stars
Week 18, 2026
All SolutionsFood fill to mark all pixels of a star | greenya | Odin Solutions
package main
import "core:fmt"
import "core:image"
import "core:image/bmp"
import "core:image/jpeg"
import "core:image/png"
_, _ :: jpeg, png
// IMAGE1_BYTES :: #load("stars-example1.png")
// IMAGE2_BYTES :: #load("stars-example2.png")
IMAGE1_BYTES :: #load("stars1.png")
IMAGE2_BYTES :: #load("stars2.jpg")
RGB_THRESHOLD :: 140 // 0..255*3
main :: proc () {
fmt.printfln("RGB threshold: %d (0-%d)", RGB_THRESHOLD, 255*3)
process_image_bytes(IMAGE1_BYTES, RGB_THRESHOLD, "result1.bmp")
process_image_bytes(IMAGE2_BYTES, RGB_THRESHOLD, "result2.bmp")
}
R_NEW :: 254
R_VISITED :: 255
process_image_bytes :: proc (bytes: [] u8, rgb_threshold: int, save_result_bmp := "") {
img, err := image.load_from_bytes(bytes)
fmt.assertf(err == nil, "Failed to load image: %v", err)
fmt.assertf(img.channels == 3, "Image must have exactly 3 channels (RGB, no alpha)")
defer image.destroy(img)
fmt.println("------------------------------")
defer fmt.println("------------------------------")
fmt.printfln("Image resolution: %d x %d", img.width, img.height)
assert(img.width * img.height == len(img.pixels.buf) / 3)
pixels := transmute ([][3] u8) img.pixels.buf[:len(img.pixels.buf)/3]
fmt.println("Applying threshold...")
star_pixel_count: int
for i in 0..<img.width*img.height {
r := int(pixels[i].r)
g := int(pixels[i].g)
b := int(pixels[i].b)
if r + g + b > RGB_THRESHOLD {
pixels[i] = {R_NEW,255,255}
star_pixel_count += 1
} else {
pixels[i] = {}
}
}
fmt.println("Counting stars...")
star_count := count_stars(pixels, img.width, img.height)
fmt.printfln("Stars : %d", star_count)
fmt.printfln("Coverage (%%) : %.1f", (f32(star_pixel_count)*100) / f32(len(pixels)))
if save_result_bmp != "" {
fmt.printfln("Writing %s...", save_result_bmp)
bmp.save(save_result_bmp, img)
}
}
count_stars :: proc (pixels: [][3] u8, width, height: int) -> (count: int) {
for y in 0..<height do for x in 0..<width {
if pixels[y*width+x].r == R_NEW {
fill_star(pixels, width, height, x, y)
count += 1
}
}
return
}
fill_star :: proc (pixels: [][3] u8, width, height, x, y: int) {
list := make([dynamic] int, len=0, cap=400)
append(&list, y*width+x)
defer delete(list)
for len(list) > 0 {
i := pop(&list)
pixels[i].r = R_VISITED
for d in ([?] [2] int {
{ -1, -1 },
{ 0, -1 },
{ +1, -1 },
{ -1, 0 },
{ +1, 0 },
{ -1, +1 },
{ 0, +1 },
{ +1, +1 },
}) {
j := i + d.y * width + d.x
if j < 0 || j >= width*height do continue
if pixels[j].r == R_NEW do append(&list, j)
}
}
}