Beispiel #1
0
char *ascii_read() {
    FILE *jpeg = webcam_read();

    image_t *original = image_read(jpeg),
            *resized  = image_new(opt_width, opt_height);

    fclose(jpeg);

    image_clear(resized);
    image_resize(original, resized);

    char *ascii = image_print(resized);

    image_destroy(original);
    image_destroy(resized);

    return ascii;
}
Beispiel #2
0
static char *
save_resized_album_art(image_s *imsrc, const char *path)
{
	int dstw, dsth;
	image_s *imdst;
	char *cache_file;
	char cache_dir[MAXPATHLEN];

	if( !imsrc )
		return NULL;

	if( art_cache_exists(path, &cache_file) )
		return cache_file;

	strncpyt(cache_dir, cache_file, sizeof(cache_dir));
	make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

	if( imsrc->width > imsrc->height )
	{
		dstw = 160;
		dsth = (imsrc->height<<8) / ((imsrc->width<<8)/160);
	}
	else
	{
		dstw = (imsrc->width<<8) / ((imsrc->height<<8)/160);
		dsth = 160;
	}
        imdst = image_resize(imsrc, dstw, dsth);
	if( !imdst )
		goto error;

	if( image_save_to_jpeg_file(imdst, cache_file) == 0 )
	{
		image_free(imdst);
		return cache_file;
	}
	else
		image_free(imdst);
error:
	free(cache_file);
	return NULL;
}
Beispiel #3
0
inline idx<Tdata> camera<Tdata>::postprocess()
{
    fps_ms_elapsed = tfps.elapsed_milliseconds();
    cntfps++;
    if (narrow_dim >= 0)
        frame = frame.narrow(narrow_dim, narrow_size, narrow_off);
    if (fps_ms_elapsed > 1000)
    {
        fps_grab = cntfps * 1000 / (float)fps_ms_elapsed;
        tfps.restart(); // restart timer
        cntfps = 0; // reset counter
    }
    if (!bresize)
        return frame; // return original frame
    else // or return a resized frame
    {
        if (mresize)
            return image_mean_resize(frame, height, width, resize_mode);
        else
            return image_resize(frame, height, width, resize_mode);
    }
}
Beispiel #4
0
/*
=================
gl_texture_create
=================
*/
erbool gl_texture_create (image_t *image, int flags, int *gltex, int *texw, int *texh)
{
    int    max, sw, sh, mip;
    GLuint tex;

    if (NULL == image || NULL == gltex || NULL == texw || NULL == texh)
    {
        sys_printf("bad args (image=%p, flags=%i, gltex=%p, texw=%p, texh=%p)\n",
                   image, flags, gltex, texw, texh);
        return false;
    }

    if (flags & GL_TEX_FL_TEX3D)
    {
        max = gl_texture3d_size_max;
    }
    else if (flags & GL_TEX_FL_CUBEMAP)
    {
        max = gl_texture_cube_map_size_max;
    }
    else
    {
        max = gl_max_texture_size;
    }

    if (GL_TEX_FL_NOPICMIP)
    {
        sw = CLAMP(image->width,  1, max);
        sh = CLAMP(image->height, 1, max);
    }
    else
    {
        sw = CLAMP(image->width  >> gl_picmip->i, 1, max);
        sh = CLAMP(image->height >> gl_picmip->i, 1, max);
    }

    if (!ext_gl_arb_texture_non_power_of_two || !gl_arb_texture_non_power_of_two->i)
    {
        sw = ceil_pwrov2(sw);
        sh = ceil_pwrov2(sh);
    }

    sw = CLAMP(sw, GL_MIN_TEXTURE_DIMENSION, max);
    sh = CLAMP(sh, GL_MIN_TEXTURE_DIMENSION, max);

    if (flags & GL_TEX_FL_NOSCALE)
    {
        *texw = sw;
        *texh = sh;

        if (!image_resize(image, sw, sh))
            return false;
    }
    else
    {
        *texw = image->width;
        *texh = image->height;

        if (!image_scale(image, sw, sh))
            return false;
    }

    glGenTextures(1, &tex);
    GLERROR();
    eglBindTexture(GL_TEXTURE_2D, tex);
    GLERROR();

    *gltex = tex;

    if (NULL != image->teximage2d)
    {
        image->teximage2d(image);
    }
    else
    {
        if (ext_gl_sgis_generate_mipmap && gl_sgis_generate_mipmap->i)
        {
#ifdef ENGINE_OS_IPHONE
            glGenerateMipmapOES(GL_TEXTURE_2D);
#else
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
#endif
            GLERROR();
            GL_IMAGE_DATA2D(0, image);
            GLERROR();
        }
        else
        {
            GL_IMAGE_DATA2D(0, image);
            GLERROR();

            for (mip = 1; image->width > 1 || image->height > 1 ; mip++)
            {
                int status;

                if (0 > (status = image_mipmap(image)))
                {
                    sys_printf("mipmap failed\n");
                    goto error;
                }
                else if (status > 0)
                {
                    break;
                }

                GL_IMAGE_DATA2D(mip, image);
                GLERROR();
            }
        }
    }

    if (flags & GL_TEX_FL_NOFILTER)
    {
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        GLERROR();
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        GLERROR();
    }
    else
    {
        glTexParameteri(GL_TEXTURE_2D,
                        GL_TEXTURE_MIN_FILTER,
                        gl_trilinear->i ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR_MIPMAP_NEAREST);
        GLERROR();
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        GLERROR();
    }

    if (!(flags & GL_TEX_FL_NOANISO) &&
            ext_gl_ext_texture_filter_anisotropic &&
            gl_ext_texture_filter_anisotropic->i)
    {
        GLfloat ani = CLAMP(gl_anisotropy_level->f, 1, gl_anisotropy_max);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, ani);
        GLERROR();
    }

    if (!(flags & GL_TEX_FL_NOLOD) &&
            ext_gl_ext_texture_lod_bias &&
            gl_ext_texture_lod_bias->i)
    {
        GLfloat lod = CLAMP(gl_lod_bias->f, 0, gl_lod_bias_max);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS_EXT, lod);
        GLERROR();
    }

    return true;

error:
    glDeleteTextures(1, &tex);
    GLERROR();

    return false;
}
Beispiel #5
0
void MainWindow::on_chooseCube_editingFinished()
{
    ui->divideButton->clicked();
    ui->picLabelRoi->clear();
    int num_cube = ui->chooseCube->value();
    if(num_cube > 0 && num_cube <= cubeRow * cubeCol)
    {
        //find cube in pic
        int num_row = (num_cube - 1) / cubeCol;//start at 0
        int num_col = (num_cube - 1) % cubeCol; // start at 0
        //qDebug() << "row,col:" << num_row << num_col;
        chooseCubeColor = image_resize(cv::Rect(num_col*3,num_row*3,3,3));//cube what we choose

//        //painting roi
//        float multiple = (float)imgScaled.width() / (float)image_resize.size().width;
//        QPicture picture0;
//        QPainter painter0;
//        QPen pen_roi;
//        pen_roi.setColor(Qt::red);
//        pen_roi.setWidth(2);
//        painter0.begin(&picture0);
//        painter0.setPen(pen_roi);
//        //painting red Roi
//        painter0.drawRect(QRectF(num_col*3*multiple,num_row*3*multiple,3*multiple,3*multiple));
//        //qDebug() << QRectF(num_col*3*multiple,num_row*3*multiple,3*multiple,3*multiple);
//        painter0.end();
//        picture0.save("draw_roi.pic");
//        ui->picLabelRoi->setPicture(picture0);


        //painting cube
        QPicture picture;
        QPainter painter;
        QPen pen_cube;
        pen_cube.setColor(Qt::black);
        pen_cube.setWidth(2);
        painter.begin(&picture);
        painter.setPen(pen_cube);
        QBrush brush(Qt::SolidPattern);

        //painting choose cube
        int block_size = 40;
        for(int i = 0; i < 9; i++)
        {
            //qDebug() << "RGB:" << chooseCubeColor.at<Vec3b>(i/3,i%3).val[0]<<chooseCubeColor.at<Vec3b>(i/3,i%3).val[1]<<chooseCubeColor.at<Vec3b>(i/3,i%3).val[2];
            brush.setColor(QColor(chooseCubeColor.at<Vec3b>(i/3,i%3).val[0],chooseCubeColor.at<Vec3b>(i/3,i%3).val[1],chooseCubeColor.at<Vec3b>(i/3,i%3).val[2]));//RGB 3 channels
            painter.setBrush(brush);
            painter.drawRoundRect(QRect(block_size*i%(block_size*3),block_size*(i/3),block_size,block_size));
        }
        painter.end();
        picture.save("draw_cube.pic");
        ui->showCubeChoose->setPicture(picture);

        //store colors of cube
        for(int i = 0; i < 9; i++)
        {
            QColor qcolor = QColor(chooseCubeColor.at<Vec3b>(i/3,i%3).val[0],chooseCubeColor.at<Vec3b>(i/3,i%3).val[1],chooseCubeColor.at<Vec3b>(i/3,i%3).val[2]);//RGB 3 channels
            int color_num;
            if(qcolor == colorTable_cube[0])
                color_num = 0;
            else if(qcolor == colorTable_cube[1])
                color_num = 1;
            else if(qcolor == colorTable_cube[2])
                color_num = 2;
            else if(qcolor == colorTable_cube[3])
                color_num = 3;
            else if(qcolor == colorTable_cube[4])
                color_num = 4;
            else if(qcolor == colorTable_cube[5])
                color_num = 5;
            else
                color_num = -1;
            col[i] = RubikColor(color_num);
            //qDebug() << "color:" << color_num;
        }
        dlg.setRubikColor(col);
    }
    else
    {
        int ret = QMessageBox::warning(this, "Warning", "Please choose the cube you want to make!", QMessageBox::Abort);
        if (ret == QMessageBox::Abort)
            qDebug() << "WARNING!!";
    }

}
Beispiel #6
0
static int lualock_lua_image_resize(lua_State *L) {
    image_t *image = luaL_checkudata(L, 1, "lualock.image");
    image_resize(image, lua_tonumber(L, 2), lua_tonumber(L, 3));
    return 0;
}
Beispiel #7
0
int main(int argc, char* argv[])
{
    printf("[i] Start...\n");

    char file_name[] = "test.bmp";
    char* filename=file_name;

    if(argc >= 2) {
        filename = argv[1];
    }

    printf("[i] file: %s\n", filename);
	
    image* img = image_create(320, 240, 3, CV_DEPTH_8U);

    if(!img) {
        printf("[!] Error: image_create()\n");
        return -1;
    }
    image_delete(&img);

    // test image loading
    image* img2 = image_load(filename);
    printf("[i] image size: %dx%dx%d (%d)\n", img2->width, img2->height, img2->n_channels, img2->size);
    image_save(img2, "test2_load_save.bmp");

    printf("[i] == Tests == \n");
#if 1
    // copy
    printf("[i] image_copy \n");
    img = image_create(img2->width, img2->height, img2->n_channels, CV_DEPTH_8U);
    image_copy(img2, img);
    image_save(img, "test2_copy.bmp");

    // test convert image to grayscale
    printf("[i] image_convert_color \n");
    image* img_gray = image_create(img2->width, img2->height, 1, CV_DEPTH_8U);
    gettimeofday(&t0, NULL);
    image_convert_color(img2, img_gray, CV_RGB2GRAY);
    gettimeofday(&t1, NULL);
    image_save(img_gray, "test3_gray.bmp");

    // test borders detection
    printf("[i] image_thin_borders \n");
    image* img_borders = NULL;
    gettimeofday(&t2, NULL);
    image_thin_borders(img_gray, &img_borders);
    gettimeofday(&t3, NULL);
    image_save(img_borders, "test4_thin_borders.bmp");

    // min-max-loc
    printf("[i] image_min_max_loc \n");
    double _min, _max;
    gettimeofday(&t4, NULL);
    image_min_max_loc(img_gray, &_min, &_max, NULL, NULL);
    gettimeofday(&t5, NULL);
    printf("[i] min=%0.2f max=%0.2f\n", _min, _max);

    // threshold
    printf("[i] image_threshold \n");
    image* img_thr = image_create(img2->width, img2->height, 1, CV_DEPTH_8U);
    gettimeofday(&t6, NULL);
    image_threshold(img_gray, img_thr, 60);
    gettimeofday(&t7, NULL);
    image_save(img_thr, "test5_threshold.bmp");
#endif

#if 1
    // rotate180
    printf("[i] image_rotate180 \n");
    image_rotate180(img2);
    image_save(img2, "test6_rotate180.bmp");
    image_rotate180(img2);
    // reflect vertical
    printf("[i] image_reflect_vertical \n");
    image_reflect_vertical(img2);
    image_save(img2, "test7_reflect_vertical.bmp");
    image_reflect_vertical(img2);
#endif // rotate180

    int colors_count;
    cv_point center;

#if 1
    // simple resize
    printf("[i] image_resize \n");
    image *img_small = image_create(160, 120, 3, CV_DEPTH_8U);
    gettimeofday(&t8, NULL);
    image_resize(img2, img_small);
    gettimeofday(&t9, NULL);
    image_save(img_small, "test8_resize.bmp");

 //   image* img_small_gray = image_create(80, 60, 1, CV_DEPTH_8U);
//    image_convert_color(img_small, img_small_gray, CV_RGB2GRAY);

//    image* img_small_borders = NULL;
//    image_thin_borders(img_small_gray, &img_small_borders);
//    image_save(img_small_borders, "test_resize_thin_borders.bmp");

#if 1
    // k-meanes colorer
    printf("[i] image_kmeans_colorer \n");
    image* img_kmeanes = image_create(160, 120, 3, CV_DEPTH_8U);
    image* img_kmeanes_idx = image_create(160, 120, 1, CV_DEPTH_8U);

#define CLUSTER_COUNT 10
    int cluster_count = CLUSTER_COUNT;
    cv_color_cluster clusters[CLUSTER_COUNT];

    gettimeofday(&t10, NULL);
    colors_count = image_kmeans_colorer(img_small, img_kmeanes, img_kmeanes_idx, clusters, cluster_count);
    gettimeofday(&t11, NULL);

    printf("[i] colors count: %d\n", colors_count);
    image_save(img_kmeanes, "test_kmeanscolorer.bmp");

#if 0
    print_color_clusters(clusters, CLUSTER_COUNT);
    printf("[i] === colors clusters after sort:\n");
    sort_color_clusters_by_count(clusters, CLUSTER_COUNT);
    print_color_clusters(clusters, CLUSTER_COUNT);
#endif

    image_delete(&img_kmeanes);
    image_delete(&img_kmeanes_idx);
#endif // k-meanes colorer

    image_delete(&img_small);
//    image_delete(&img_small_gray);
//    image_delete(&img_small_borders);
#endif // simple resize

#if 1
    // HSV
    printf("[i] image_hsv2rgb \n");
    image* img_hsv = image_create(img2->width, img2->height, 3, CV_DEPTH_8U);
    image* img_bgr = image_create(img2->width, img2->height, 3, CV_DEPTH_8U);

    gettimeofday(&t12, NULL);
    image_rgb2hsv(img2, img_hsv);
    gettimeofday(&t13, NULL);

    image_hsv2rgb(img_hsv, img_bgr);

    image_save(img_hsv, "test9_rgb2hsv.bmp");
    image_save(img_bgr, "test9_hsv2rgb.bmp");

    image_delete(&img_hsv);
    image_delete(&img_bgr);
#endif // HSV

#if 1
    // hsv colorer
    printf("[i] image_hsv_colorer \n");
    image* img_hsv_col = image_create(img2->width, img2->height, 3, CV_DEPTH_8U);
    image* img_hsv_idx = image_create(img2->width, img2->height, 1, CV_DEPTH_8U);

#define COLORS_COUNT 10
    cv_color_cluster clusters2[COLORS_COUNT];

    gettimeofday(&t14, NULL);
    colors_count = image_hsv_colorer(img2, img_hsv_col, img_hsv_idx, clusters2, COLORS_COUNT);
    gettimeofday(&t15, NULL);

    printf("[i] colors count: %d\n", colors_count);
    image_save(img_hsv_col, "test_hsvcolorer.bmp");

#if 1
    print_color_clusters(clusters2, COLORS_COUNT);
    printf("[i] === colors clusters after sort:\n");
    sort_color_clusters_by_count(clusters2, COLORS_COUNT);
    print_color_clusters(clusters2, COLORS_COUNT);

    center = get_color_center(clusters2[0].id, img_hsv_idx);
    printf("[i] first color center:  %03d %03d\n", center.x, center.y);
    center = get_color_center(clusters2[1].id, img_hsv_idx);
    printf("[i] second color center: %03d %03d\n", center.x, center.y);
    center = get_color_center(clusters2[2].id, img_hsv_idx);
    printf("[i] third color center:  %03d %03d\n", center.x, center.y);
#endif // print_color_clusters

    image_delete(&img_hsv_col);
    image_delete(&img_hsv_idx);
#endif // hsv colorer

    printf("[i] == Performance == \n");
    print_performance("image_convert_color", t1, t0);
    print_performance("image_thin_borders", t3, t2);
    print_performance("image_min_max_loc", t5, t4);
    print_performance("image_threshold", t7, t6);
    print_performance("image_resize", t9, t8);
    print_performance("image_kmeans_colorer", t11, t10);
    print_performance("image_rgb2hsv", t13, t12);
    print_performance("image_hsv_colorer", t15, t14);

    image_delete(&img);
    image_delete(&img2);
#if 1
    image_delete(&img_gray);
    image_delete(&img_borders);
    image_delete(&img_thr);
#endif

    printf("[i] End.\n");
    return 0;
}
Beispiel #8
0
static void
set_user_icon_from_png (char *path, uint32_t bgcolor)
{
	int j, alpha;
	image_s *img = image_new_from_png (path, 1, NULL, 0, 1, 0, &alpha);
	image_s *img_sm, *img_lrg;
	struct icon_struct newicons;
	double n, scale_lrg, scale_sm;

	if (img == (image_s *)NULL)
	{
		DPRINTF (E_WARN, L_GENERAL,
				"Unable to load icon file \"%s\".\n", path);
		return;
	}

	n = (img->height > img->width) ? img->height : img->width;
	scale_lrg = 120.0 / n;
	scale_sm = 48.0 / n;

	if ((img_lrg = image_resize (img,
					xround (img->width*scale_lrg),
					xround(img->height*scale_lrg))) == (image_s *)NULL)
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to rescale large icon image (%s).\n", path);
		image_free (img);
		return;
	}

	if ((img_sm = image_resize (img,
					xround(img->width*scale_sm),
					xround(img->height*scale_sm))) == (image_s *)NULL)
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to rescale small icon image (%s).\n", path);
		image_free (img);
		image_free (img_lrg);
		return;
	}

	image_free (img);

	for (j = ICON_FIRST; j <= ICON_LAST; j++)
		newicons.dynamic[j] = 1;

	if ((newicons.size[ICON_PNG_LRG] = image_save_to_png (img_lrg, NULL,
			&newicons.icon[ICON_PNG_LRG], alpha, 9)) < 0)
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to create large PNG icon (%s).\n", path);
		newicons.icon[ICON_PNG_LRG] = icons.icon[ICON_PNG_LRG];
		newicons.size[ICON_PNG_LRG] = icons.size[ICON_PNG_LRG];
		newicons.dynamic[ICON_PNG_LRG] = icons.dynamic[ICON_PNG_LRG];
	}

	if ((newicons.size[ICON_PNG_SM] = image_save_to_png (img_sm, NULL,
			&newicons.icon[ICON_PNG_SM], alpha, 9)) < 0)
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to create small PNG icon (%s).\n", path);
		newicons.icon[ICON_PNG_SM] = icons.icon[ICON_PNG_SM];
		newicons.size[ICON_PNG_SM] = icons.size[ICON_PNG_SM];
		newicons.dynamic[ICON_PNG_SM] = icons.dynamic[ICON_PNG_SM];
	}

	if (alpha)
	{
		image_bgcolor_composite (img_lrg, bgcolor, -1);
		image_bgcolor_composite (img_sm, bgcolor, -1);
	}

	if (!(newicons.icon[ICON_JPEG_LRG] = image_save_to_jpeg_buf (img_lrg,
			&newicons.size[ICON_JPEG_LRG])))
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to create large JPEG icon (%s).\n", path);
		newicons.icon[ICON_JPEG_LRG] = icons.icon[ICON_JPEG_LRG];
		newicons.size[ICON_JPEG_LRG] = icons.size[ICON_JPEG_LRG];
		newicons.dynamic[ICON_JPEG_LRG] = icons.dynamic[ICON_JPEG_LRG];
	}

	if (!(newicons.icon[ICON_JPEG_SM] = image_save_to_jpeg_buf (img_sm,
			&newicons.size[ICON_JPEG_SM])))
	{
		DPRINTF (E_ERROR, L_GENERAL,
				"Failed to create small JPEG icon (%s).\n", path);
		newicons.icon[ICON_JPEG_SM] = icons.icon[ICON_JPEG_SM];
		newicons.size[ICON_JPEG_SM] = icons.size[ICON_JPEG_SM];
		newicons.dynamic[ICON_JPEG_SM] = icons.dynamic[ICON_JPEG_SM];
	}

	image_free (img_sm);
	image_free (img_lrg);
	destroy_icons (&icons);
	icons = newicons;
}
Beispiel #9
0
bool_t model_md2_save(const model_t *orig_model, xbuf_t *xbuf, char **out_error)
{
	char *error;
	int skinwidth, skinheight;
	char **skinfilenames;
	const skininfo_t *skininfo;
	model_t *model;
	const mesh_t *mesh;
	md2_data_t *md2data;
	md2_header_t *header;
	dtriangle_t *dtriangles;
	int i, j, k;

	model = model_merge_meshes(orig_model);

	mesh = &model->meshes[0];

	skinwidth = (texwidth != -1) ? texwidth : 0;
	skinheight = (texheight != -1) ? texheight : 0;
	for (i = 0, skininfo = model->skininfo; i < model->num_skins; i++, skininfo++)
	{
		for (j = 0; j < skininfo->num_skins; j++)
		{
			int offset = skininfo->skins[j].offset;

			if (!mesh->skins[offset].components[SKIN_DIFFUSE])
			{
				if (out_error)
					*out_error = msprintf("Model has missing skin.");
				model_free(model);
				return false;
			}

			if (skinwidth && skinheight && (skinwidth != mesh->skins[offset].components[SKIN_DIFFUSE]->width || skinheight != mesh->skins[offset].components[SKIN_DIFFUSE]->height))
			{
				if (out_error)
					*out_error = msprintf("Model has skins of different sizes. Use -texwidth and -texheight to resize all images to the same size");
				model_free(model);
				return false;
			}
			skinwidth = mesh->skins[offset].components[SKIN_DIFFUSE]->width;
			skinheight = mesh->skins[offset].components[SKIN_DIFFUSE]->height;

		/* if fullbright texture is a different size, resample it to match the diffuse texture */
			if (mesh->skins[offset].components[SKIN_FULLBRIGHT] && (mesh->skins[offset].components[SKIN_FULLBRIGHT]->width != skinwidth || mesh->skins[offset].components[SKIN_FULLBRIGHT]->height != skinheight))
			{
				image_rgba_t *image = image_resize(mem_globalpool, mesh->skins[offset].components[SKIN_FULLBRIGHT], skinwidth, skinheight);
				image_free(&mesh->skins[offset].components[SKIN_FULLBRIGHT]);
				mesh->skins[offset].components[SKIN_FULLBRIGHT] = image;
			}
		}
	}

	if (!skinwidth || !skinheight)
	{
		if (out_error)
			*out_error = msprintf("Model has no skin. Use -texwidth and -texheight to set the skin dimensions, or -tex to import a skin");
		model_free(model);
		return false;
	}

/* create 8-bit skins and save them to PCX files */
	skinfilenames = (char**)qmalloc(sizeof(char*) * model->num_skins);

	for (i = 0, skininfo = model->skininfo; i < model->num_skins; i++, skininfo++)
	{
		image_paletted_t *pimage;

		int offset = skininfo->skins[0].offset; /* skingroups not supported, just take the first skin from the group */

		skinfilenames[i] = md2_create_skin_filename(skininfo->skins[0].name);

		pimage = image_palettize(mem_globalpool, &palette_quake2, mesh->skins[offset].components[SKIN_DIFFUSE], mesh->skins[offset].components[SKIN_FULLBRIGHT]);

	/* FIXME - this shouldn't be a fatal error */
		if (!image_paletted_save(skinfilenames[i], pimage, &error))
		{
			if (out_error)
				*out_error = msprintf("Failed to write %s: %s", skinfilenames[i], error);
			qfree(error);
			qfree(pimage);
			for (j = 0; j < i; j++)
				qfree(skinfilenames[j]);
			qfree(skinfilenames);
			model_free(model);
			return false;
		}

		qfree(pimage);
	}

/* optimize vertices for md2 format */
	md2data = md2_process_vertices(model, mesh, skinwidth, skinheight);

/* write header */
	header = (md2_header_t*)xbuf_reserve_data(xbuf, sizeof(md2_header_t));

	memcpy(header->ident, "IDP2", 4);
	header->version       = LittleLong(8);
	header->skinwidth     = LittleLong(skinwidth);
	header->skinheight    = LittleLong(skinheight);
	header->framesize     = LittleLong(sizeof(daliasframe_t) + sizeof(dtrivertx_t) * md2data->numvertices);
	header->num_skins     = LittleLong(model->num_skins);
	header->num_vertices  = LittleLong(md2data->numvertices);
	header->num_st        = LittleLong(md2data->numtexcoords);
	header->num_tris      = LittleLong(mesh->num_triangles);
	header->num_glcmds    = 0; /* filled in later */
	header->num_frames    = LittleLong(model->num_frames);
	header->offset_skins  = 0;
	header->offset_st     = 0;
	header->offset_tris   = 0;
	header->offset_frames = 0;
	header->offset_glcmds = 0;
	header->offset_end    = 0;

/* write skins */
	header->offset_skins = LittleLong(xbuf_get_bytes_written(xbuf));

	for (i = 0; i < model->num_skins; i++)
	{
		md2_skin_t md2skin;
		
		strlcpy(md2skin.name, skinfilenames[i], sizeof(md2skin.name));

		xbuf_write_data(xbuf, sizeof(md2_skin_t), &md2skin);
	}

/* write texcoords */
	for (i = 0; i < md2data->numtexcoords; i++)
	{
		md2data->texcoords[i].s = LittleShort(md2data->texcoords[i].s);
		md2data->texcoords[i].t = LittleShort(md2data->texcoords[i].t);
	}

	header->offset_st = LittleLong(xbuf_get_bytes_written(xbuf));

	xbuf_write_data(xbuf, sizeof(dstvert_t) * md2data->numtexcoords, md2data->texcoords);

/* write triangles */
	dtriangles = (dtriangle_t*)qmalloc(sizeof(dtriangle_t) * mesh->num_triangles);
	for (i = 0; i < mesh->num_triangles; i++)
	{
		for (j = 0; j < 3; j++)
		{
			dtriangles[i].index_xyz[j] = LittleShort(md2data->vertex_lookup[mesh->triangle3i[i*3+j]]);
			dtriangles[i].index_st[j] = LittleShort(md2data->texcoord_lookup[mesh->triangle3i[i*3+j]]);
		}
	}

	header->offset_tris = LittleLong(xbuf_get_bytes_written(xbuf));

	xbuf_write_data(xbuf, sizeof(dtriangle_t) * mesh->num_triangles, dtriangles);

/* write frames */
	header->offset_frames = LittleLong(xbuf_get_bytes_written(xbuf));

	for (i = 0; i < model->num_frames; i++)
	{
		daliasframe_t md2frame;

		for (j = 0; j < 3; j++)
		{
			md2frame.scale[j] = LittleFloat(md2data->frames[i].scale[j]);
			md2frame.translate[j] = LittleFloat(md2data->frames[i].translate[j]);
		}

		strlcpy(md2frame.name, model->frameinfo[i].frames[0].name, sizeof(md2frame.name));

		xbuf_write_data(xbuf, sizeof(daliasframe_t), &md2frame);

	/* write vertices */
		for (j = 0; j < md2data->numvertices; j++)
		{
			const dtrivertx_t *md2vertex = md2data->original_vertices + md2data->vertices[j] * model->num_frames + i;
			dtrivertx_t vtx;

			for (k = 0; k < 3; k++)
				vtx.v[k] = md2vertex->v[k];

			vtx.lightnormalindex = md2vertex->lightnormalindex;

			xbuf_write_data(xbuf, sizeof(dtrivertx_t), &vtx);
		}
	}

/* write glcmds */
	triangles = dtriangles;
	num_tris = mesh->num_triangles;

	md2_build_glcmds(md2data->texcoords, skinwidth, skinheight);

	header->num_glcmds = LittleLong(numcommands);
	header->offset_glcmds = LittleLong(xbuf_get_bytes_written(xbuf));

	for (i = 0; i < numcommands; i++)
		commands[i] = LittleLong(commands[i]);
	xbuf_write_data(xbuf, sizeof(int) * numcommands, commands);

/* write end */
	header->offset_end = LittleLong(xbuf_get_bytes_written(xbuf));

/* done */
	qfree(dtriangles);
	md2_free_data(md2data);

	for (i = 0; i < model->num_skins; i++)
		qfree(skinfilenames[i]);
	qfree(skinfilenames);

	model_free(model);
	return true;
}