void MapEditor::calculate_light(bool pixel_precise) {
    if (wmap) {
        EditableMap::Lights& lights = wmap->get_light_sources();
        if (!lights.size()) {
            show_messagebox(Gui::MessageBoxIconExclamation, "Light Sources",
                "No light sources found in this map.");
            return;
        }

        /* name set? */
        if (!wmap->get_name().length()) {
            show_messagebox(Gui::MessageBoxIconExclamation, "No Map Name",
                "Please choose a map name in the map properties.");
            return;
        }

        /* trace all points */
        Tileset *ts = wmap->get_tileset_ptr();
        if (!ts) {
            show_messagebox(Gui::MessageBoxIconExclamation, "No Tileset",
                "Please select a tileset in the map properties.");
            return;
        }

        int vw = subsystem.get_view_width();
        int vh = subsystem.get_view_height();
        int ww = 127;
        int wh = 80; //60;
        win_compile = push_window(vw / 2 - ww / 2, vh / 2 - wh / 2, ww, wh, "Compiling");
        lbl_compile = create_label(win_compile, Spc, Spc, "0%");
        GuiButton *btn = add_close_button(win_compile, static_cancel_compilation_click);
        btn->set_caption("Cancel");

        int mw = wmap->get_width();
        int mh = wmap->get_height();
        int tw = ts->get_tile_width();
        int th = ts->get_tile_height();
        lightmap_w = mw * tw;
        lightmap_h = mh * th;
        int mapw = mw * tw;
        int maph = mh * th;
        short **pdeco = wmap->get_decoration();

        if (lightmap_w % LightMapSize) {
            lightmap_w = ((lightmap_w / LightMapSize) + 1) * LightMapSize;
        }
        if (lightmap_h % LightMapSize) {
            lightmap_h = ((lightmap_h / LightMapSize) + 1) * LightMapSize;
        }

        /* create whole lightmap */
        lightmap = new unsigned char *[lightmap_h];

        /* create thread */
        if (pixel_precise) {
            for (int y = 0; y < lightmap_h; y++) {
                lightmap[y] = new unsigned char[lightmap_w * 4];
                for (int x = 0; x < lightmap_w; x++) {
                    lightmap[y][x * 4 + 0] = 0;
                    lightmap[y][x * 4 + 1] = 0;
                    lightmap[y][x * 4 + 2] = 0;
                    if (x >= mapw || y >= maph) {
                        lightmap[y][x * 4 + 3] = 0;
                    } else {
                        int index = pdeco[y / th][x / tw];
                        if (index < 0) {
                            lightmap[y][x * 4 + 3] = 0;
                        } else {
                            TileGraphic *tg = ts->get_tile(index)->get_tilegraphic();
                            TileGraphicGL *tggl = static_cast<TileGraphicGL *>(tg);
                            if (tggl->get_bytes_per_pixel(0) < 4) {
                                lightmap[y][x * 4 + 3] = 255;
                            } else {
                                unsigned char *p = tggl->get_picture_array(0);
                                p += (((y % th) * 4 * tw) + ((x % tw) * 4));
                                lightmap[y][x * 4 + 3] = p[3];
                            }
                        }
                    }

                }
            }
            compile_thread = new CompileThreadPixel(wmap, lightmap);
        } else {
            for (int y = 0; y < lightmap_h; y++) {
                lightmap[y] = new unsigned char[lightmap_w * 4];
                for (int x = 0; x < lightmap_w; x++) {
                    lightmap[y][x * 4 + 0] = 0;
                    lightmap[y][x * 4 + 1] = 0;
                    lightmap[y][x * 4 + 2] = 0;
                    if (x >= mapw || y >= maph) {
                        lightmap[y][x * 4 + 3] = 0;
                    } else {
                        if (pdeco[y / th][x / tw] < 0) {
                            lightmap[y][x * 4 + 3] = 0;
                        } else {
                            lightmap[y][x * 4 + 3] = 255;
                        }
                    }

                }
            }
            compile_thread = new CompileThreadBlock(wmap, lightmap);
        }
    }
}
void CompileThreadBlock::thread() {
    EditableMap::Lights& lights = wmap->get_light_sources();
    Tileset *ts = wmap->get_tileset_ptr();
    size_t nlgt = lights.size();
    int mw = wmap->get_width();
    int mh = wmap->get_height();
    int tw = ts->get_tile_width();
    int th = ts->get_tile_height();
    int w = mw * tw;
    int h = mh * th;
    short **pmap = wmap->get_map();
    short **pdeco = wmap->get_decoration();
    Point pr;

    for (size_t i = 0; i < nlgt; i++) {
        {
            Scope<Mutex> lock(mtx);
            finished_percent = 100 * (i + 1) / nlgt;
            if (cancelled) {
                break;
            }
        }
        int r = lights[i]->radius;
        int colr = lights[i]->r;
        int colg = lights[i]->g;
        int colb = lights[i]->b;
        int lmaxsq = r * r;
        int lx = lights[i]->x;
        int ly = lights[i]->y;
        Point p2(static_cast<float>(lx * tw + (tw / 2)), static_cast<float>(ly * th + (th / 2)));
        int lsx = static_cast<int>(p2.x) - r;
        int lsy = static_cast<int>(p2.y) - r;
        int lex = static_cast<int>(p2.x) + r;
        int ley = static_cast<int>(p2.y) + r;
        if (lsx < 0) lsx = 0;
        if (lsy < 0) lsy = 0;
        if (lex > w) lex = w;
        if (ley > h) ley = h;

        int txs = lsx / tw;
        int txe = lex / tw;
        int tys = lsy / th;
        int tye = ley / th;

        for (int y = lsy; y < ley; y++) {
            for (int x = lsx; x < lex; x++) {
                int dindex = pdeco[y / th][x / tw];
                if (dindex < 0) {
                    lightmap[y][x * 4 + 3] = 0;
                } else {
                    bool contact = false;
                    Point p1(static_cast<float>(x), static_cast<float>(y));
                    float xd = p2.x - p1.x;
                    float yd = p2.y - p1.y;
                    float dist = xd * xd + yd * yd;
                    if (dist < lmaxsq) {
                        for (int tx = txs; tx < txe; tx++) {
                            for (int ty = tys; ty < tye; ty++) {
                                short index = pmap[ty][tx];
                                if (index >= 0) {
                                    if (ts->get_tile(index)->is_light_blocking()) {
                                        Point p1l(static_cast<float>(tx * tw), static_cast<float>(ty * th));
                                        Point p2l(static_cast<float>(tx * tw), static_cast<float>((ty + 1) * th - 0.5f));
                                        Point p1r(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>(ty * th));
                                        Point p2r(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>((ty + 1) * th - 0.5f));
                                        Point p1t(static_cast<float>(tx * tw), static_cast<float>(ty * th));
                                        Point p2t(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>(ty * th));
                                        Point p1b(static_cast<float>(tx * tw), static_cast<float>((ty + 1) * th - 0.5f));
                                        Point p2b(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>((ty + 1) * th - 0.5f));
                                        if (intersection(p1, p2, p1l, p2l, pr) ||
                                            intersection(p1, p2, p1r, p2r, pr) ||
                                            intersection(p1, p2, p1t, p2t, pr) ||
                                            intersection(p1, p2, p1b, p2b, pr))
                                        {
                                            contact = true;
                                            break;
                                        }
                                    }
                                }
                            }
                            if (contact) {
                                break;
                            }
                        }
                    } else {
                        contact = true;
                    }
                    if (!contact) {
                        int v = static_cast<int>(sqrt(65025.0f * dist / lmaxsq));
                        if (v < lightmap[y][x * 4 + 3]) {
                            double l = 1.0 - (static_cast<double>(v) / 255.0);
                            lightmap[y][x * 4 + 0] = static_cast<unsigned char>(colr * l);
                            lightmap[y][x * 4 + 1] = static_cast<unsigned char>(colg * l);
                            lightmap[y][x * 4 + 2] = static_cast<unsigned char>(colb * l);
                            lightmap[y][x * 4 + 3] = v;
                        }
                    }
                }
            }
        }
    }

    Scope<Mutex> lock(mtx);
    finished = true;
}
void CompileThreadPixel::thread() {
    EditableMap::Lights& lights = wmap->get_light_sources();
    Tileset *ts = wmap->get_tileset_ptr();
    size_t nlgt = lights.size();
    int mw = wmap->get_width();
    int mh = wmap->get_height();
    int tw = ts->get_tile_width();
    int th = ts->get_tile_height();
    int w = mw * tw;
    int h = mh * th;
    short **pmap = wmap->get_map();
    short **pdeco = wmap->get_decoration();
    Point pr;

    for (size_t i = 0; i < nlgt; i++) {
        {
            ScopeMutex lock(mtx);
            finished_percent = 100 * (i + 1) / nlgt;
        }
        int r = lights[i]->radius;
        int lmaxsq = r * r;
        int lx = lights[i]->x;
        int ly = lights[i]->y;
        Point p2(static_cast<float>(lx * tw + (tw / 2)), static_cast<float>(ly * th + (th / 2)));
        int lsx = static_cast<int>(p2.x) - r;
        int lsy = static_cast<int>(p2.y) - r;
        int lex = static_cast<int>(p2.x) + r;
        int ley = static_cast<int>(p2.y) + r;
        if (lsx < 0) lsx = 0;
        if (lsy < 0) lsy = 0;
        if (lex > w) lex = w;
        if (ley > h) ley = h;

        int txs = lsx / tw;
        int txe = lex / tw;
        int tys = lsy / th;
        int tye = ley / th;

        for (int y = lsy; y < ley; y++) {
            for (int x = lsx; x < lex; x++) {
                int dindex = pdeco[y / th][x / tw];
                if (dindex < 0) {
                    lightmap[y][x * 4 + 3] = 0;
                } else {
                    bool contact = false;
                    Point p1(static_cast<float>(x), static_cast<float>(y));
                    float xd = p2.x - p1.x;
                    float yd = p2.y - p1.y;
                    float dist = xd * xd + yd * yd;
                    if (dist < lmaxsq) {
                        for (int tx = txs; tx < txe; tx++) {
                            for (int ty = tys; ty < tye; ty++) {
                                short index = pmap[ty][tx];
                                if (index >= 0) {
                                    if (ts->get_tile(index)->is_light_blocking()) {
                                        TileGraphic *tg = ts->get_tile(index)->get_tilegraphic();
                                        TileGraphicGL *tggl = static_cast<TileGraphicGL *>(tg);
                                        if (tggl->get_bytes_per_pixel(0) < 4) {
                                            Point p1l(static_cast<float>(tx * tw), static_cast<float>(ty * th));
                                            Point p2l(static_cast<float>(tx * tw), static_cast<float>((ty + 1) * th - 0.5f));
                                            Point p1r(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>(ty * th));
                                            Point p2r(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>((ty + 1) * th - 0.5f));
                                            Point p1t(static_cast<float>(tx * tw), static_cast<float>(ty * th));
                                            Point p2t(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>(ty * th));
                                            Point p1b(static_cast<float>(tx * tw), static_cast<float>((ty + 1) * th - 0.5f));
                                            Point p2b(static_cast<float>((tx + 1) * tw - 0.5f), static_cast<float>((ty + 1) * th - 0.5f));
                                            if (intersection(p1, p2, p1l, p2l, pr) ||
                                                intersection(p1, p2, p1r, p2r, pr) ||
                                                intersection(p1, p2, p1t, p2t, pr) ||
                                                intersection(p1, p2, p1b, p2b, pr))
                                            {
                                                contact = true;
                                                break;
                                            }
                                        } else {
                                            unsigned char *p = tggl->get_picture_array(0);
                                            for (int py = 0; py < th; py++) {
                                                for (int px = 0; px < tw; px++) {
                                                    if (p[3] == 255) {
                                                        Point p1v(static_cast<float>(tx * tw + px) + 0.5f, static_cast<float>(ty * th + py) - 0.5f);
                                                        Point p2v(static_cast<float>(tx * tw + px) + 0.5f, static_cast<float>(ty * th + py) + 0.5f);
                                                        Point p1h(static_cast<float>(tx * tw + px) - 0.5f, static_cast<float>(ty * th + py) + 0.5f);
                                                        Point p2h(static_cast<float>(tx * tw + px) + 0.5f, static_cast<float>(ty * th + py) + 0.5f);
                                                        if (intersection(p1, p2, p1v, p2v, pr) ||
                                                            intersection(p1, p2, p1h, p2h, pr))
                                                        {
                                                            contact = true;
                                                            break;
                                                        }
                                                    }
                                                    p += 4;
                                                }
                                                if (contact) {
                                                   break;
                                                }
                                            }
                                            if (contact) {
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            if (contact) {
                                break;
                            }
                        }
                    } else {
                        contact = true;
                    }
                    if (!contact) {
                        int v = static_cast<int>(sqrt(65025.0f * dist / lmaxsq));
                        if (v < lightmap[y][x * 4 + 3]) {
                            lightmap[y][x * 4 + 3] = v;
                        }
                    }
                }
            }
        }
    }

    ScopeMutex lock(mtx);
    finished = true;
}