mirror of
https://gitlab.winehq.org/wine/fontforge.git
synced 2025-02-18 13:09:44 +00:00
375 lines
11 KiB
C
375 lines
11 KiB
C
/* Copyright (C) 2000-2012 by George Williams */
|
|
/*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include "gimage.h"
|
|
|
|
GImage *GImageCreate(enum image_type type, int32 width, int32 height) {
|
|
GImage *gi;
|
|
struct _GImage *base;
|
|
|
|
if ( type<it_mono || type>it_rgba )
|
|
return( NULL );
|
|
|
|
gi = gcalloc(1,sizeof(GImage));
|
|
base = galloc(sizeof(struct _GImage));
|
|
if ( gi==NULL || base==NULL ) {
|
|
free(gi); free(base);
|
|
return( NULL );
|
|
}
|
|
gi->u.image = base;
|
|
base->image_type = type;
|
|
base->width = width;
|
|
base->height = height;
|
|
base->bytes_per_line = (type==it_true || type==it_rgba)?4*width:type==it_index?width:(width+7)/8;
|
|
base->data = NULL;
|
|
base->clut = NULL;
|
|
base->trans = COLOR_UNKNOWN;
|
|
base->data = galloc(height*base->bytes_per_line);
|
|
if ( base->data==NULL ) {
|
|
free(base);
|
|
free(gi);
|
|
return( NULL );
|
|
}
|
|
if ( type==it_index ) {
|
|
base->clut = gcalloc(1,sizeof(GClut));
|
|
base->clut->trans_index = COLOR_UNKNOWN;
|
|
}
|
|
return( gi );
|
|
}
|
|
|
|
|
|
GImage *_GImage_Create(enum image_type type, int32 width, int32 height) {
|
|
GImage *gi;
|
|
struct _GImage *base;
|
|
|
|
if ( type<it_mono || type>it_rgba )
|
|
return( NULL );
|
|
|
|
gi = gcalloc(1,sizeof(GImage));
|
|
base = galloc(sizeof(struct _GImage));
|
|
if ( gi==NULL || base==NULL ) {
|
|
free(gi); free(base);
|
|
return( NULL );
|
|
}
|
|
gi->u.image = base;
|
|
base->image_type = type;
|
|
base->width = width;
|
|
base->height = height;
|
|
base->bytes_per_line = (type==it_true || type==it_rgba)?4*width:type==it_index?width:(width+7)/8;
|
|
base->data = NULL;
|
|
base->clut = NULL;
|
|
if ( type==it_index )
|
|
base->clut = gcalloc(1,sizeof(GClut));
|
|
return( gi );
|
|
}
|
|
|
|
void GImageDestroy(GImage *gi) {
|
|
int i;
|
|
|
|
if ( gi->list_len!=0 ) {
|
|
for ( i=0; i<gi->list_len; ++i ) {
|
|
free(gi->u.images[i]->clut);
|
|
free(gi->u.images[i]->data);
|
|
free(gi->u.images[i]);
|
|
}
|
|
free(gi->u.images);
|
|
} else {
|
|
free(gi->u.image->clut);
|
|
free(gi->u.image->data);
|
|
free(gi->u.image);
|
|
}
|
|
free(gi);
|
|
}
|
|
|
|
GImage *GImageCreateAnimation(GImage **images, int n) {
|
|
struct _GImage **imgs = galloc(n*sizeof(struct _GImage *));
|
|
GImage *gi = gcalloc(1,sizeof(GImage));
|
|
int i;
|
|
|
|
gi->list_len = n;
|
|
gi->u.images = imgs;
|
|
for ( i=0; i<n; ++i ) {
|
|
if ( images[i]->list_len!=0 ) {
|
|
free(gi);
|
|
return( NULL );
|
|
}
|
|
if ( images[i]->u.image->image_type!=images[0]->u.image->image_type )
|
|
return( NULL );
|
|
imgs[i] = images[i]->u.image;
|
|
free(images[i]);
|
|
}
|
|
return( gi );
|
|
}
|
|
|
|
/* -1 => add it at the end */
|
|
GImage *GImageAddImageBefore(GImage *dest, GImage *src, int pos) {
|
|
struct _GImage **imgs;
|
|
int n, i, j;
|
|
enum image_type it;
|
|
|
|
n = (src->list_len==0?1:src->list_len) + (dest->list_len==0?1:dest->list_len);
|
|
imgs = galloc(n*sizeof(struct _GImage *));
|
|
|
|
i = 0;
|
|
if ( dest->list_len==0 ) {
|
|
it = dest->u.image->image_type;
|
|
if ( pos==-1 ) pos = 1;
|
|
if ( pos!=0 ) imgs[i++] = dest->u.image;
|
|
} else {
|
|
it = dest->u.images[0]->image_type;
|
|
if ( pos==-1 ) pos = dest->list_len;
|
|
for ( i=0; i<pos; ++i )
|
|
imgs[i] = dest->u.images[i];
|
|
}
|
|
j = i;
|
|
if ( src->list_len==0 ) {
|
|
if ( src->u.image->image_type!=it )
|
|
return( NULL );
|
|
imgs[j++] = src->u.image;
|
|
} else {
|
|
for ( ; j<i+src->list_len; ++j ) {
|
|
if ( src->u.images[j-i]->image_type!=it )
|
|
return( NULL );
|
|
imgs[j] = src->u.images[j-i];
|
|
}
|
|
free(src->u.images);
|
|
}
|
|
if ( dest->list_len==0 ) {
|
|
if ( pos==0 ) imgs[j++] = dest->u.image;
|
|
} else {
|
|
for ( ; j<n; ++j )
|
|
imgs[j] = dest->u.images[i++];
|
|
}
|
|
dest->u.images = imgs;
|
|
dest->list_len = n;
|
|
free(src);
|
|
return( dest );
|
|
}
|
|
|
|
void GImageDrawRect(GImage *img,GRect *r,Color col) {
|
|
struct _GImage *base;
|
|
int i;
|
|
|
|
base = img->u.image;
|
|
if ( r->y>=base->height || r->x>=base->width )
|
|
return;
|
|
|
|
for ( i=0; i<r->width; ++i ) {
|
|
if ( i+r->x>=base->width )
|
|
break;
|
|
base->data[r->y*base->bytes_per_line + i + r->x] = col;
|
|
if ( r->y+r->height-1<base->height )
|
|
base->data[(r->y+r->height-1)*base->bytes_per_line + i + r->x] = col;
|
|
}
|
|
for ( i=0; i<r->height; ++i ) {
|
|
if ( i+r->y>=base->height )
|
|
break;
|
|
base->data[(r->y+i)*base->bytes_per_line + r->x] = col;
|
|
if ( r->x+r->width-1<base->width )
|
|
base->data[(r->y+i)*base->bytes_per_line + r->x+r->width-1] = col;
|
|
}
|
|
}
|
|
|
|
void GImageDrawImage(GImage *dest,GImage *src,void *junk,int x, int y) {
|
|
struct _GImage *sbase, *dbase;
|
|
int i,j, di, sbi, dbi, val, factor, maxpix, sbit;
|
|
|
|
/* This is designed to merge images which should be treated as alpha */
|
|
/* channels. dest must be indexed, src may be either indexed or mono */
|
|
dbase = dest->u.image;
|
|
sbase = src->u.image;
|
|
|
|
if ( dbase->image_type != it_index ) {
|
|
fprintf( stderr, "Bad call to GImageMaxImage\n" );
|
|
return;
|
|
}
|
|
|
|
maxpix = 1;
|
|
if ( dbase->clut!=NULL )
|
|
maxpix = dbase->clut->clut_len - 1;
|
|
|
|
if ( dbase->clut!=NULL && sbase->clut!=NULL && sbase->clut->clut_len>1 ) {
|
|
factor = (dbase->clut->clut_len - 1) / (sbase->clut->clut_len - 1);
|
|
if ( factor==0 ) factor=1;
|
|
} else
|
|
factor = 1;
|
|
|
|
if ( sbase->image_type == it_index ) {
|
|
for ( i=0; i<sbase->height; ++i ) {
|
|
di = y + i;
|
|
if ( di<0 || di>=dbase->height )
|
|
continue;
|
|
sbi = i*sbase->bytes_per_line;
|
|
dbi = di*dbase->bytes_per_line;
|
|
for ( j=0; j<sbase->width; ++j ) {
|
|
if ( x+j<0 || x+j>=dbase->width )
|
|
continue;
|
|
val = dbase->data[dbi+x+j] + sbase->data[sbi+j]*factor;
|
|
if ( val>255 ) val = 255;
|
|
dbase->data[dbi+x+j] = val;
|
|
}
|
|
}
|
|
} else if ( sbase->image_type == it_mono ) {
|
|
for ( i=0; i<sbase->height; ++i ) {
|
|
di = y + i;
|
|
if ( di<0 || di>=dbase->height )
|
|
continue;
|
|
sbi = i*sbase->bytes_per_line;
|
|
dbi = di*dbase->bytes_per_line;
|
|
for ( j=0, sbit=0x80; j<sbase->width; ++j ) {
|
|
if ( x+j<0 || x+j>=dbase->width )
|
|
continue;
|
|
if ( sbase->data[sbi+(j>>3)] & sbit )
|
|
dbase->data[dbi+x+j] = maxpix;
|
|
if ( (sbit>>=1) == 0 )
|
|
sbit = 0x80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Blends src image with alpha channel over dest. Both images must be */
|
|
/* 32-bit truecolor. Alpha channel of dest must be all opaque. */
|
|
void GImageBlendOver(GImage *dest,GImage *src,GRect *from,int x, int y) {
|
|
struct _GImage *sbase, *dbase;
|
|
int i, j, a, r, g, b;
|
|
uint32 *dpt, *spt;
|
|
|
|
dbase = dest->u.image;
|
|
sbase = src->u.image;
|
|
|
|
if ( dbase->image_type != it_true ) {
|
|
fprintf( stderr, "Bad call to GImageBlendOver\n" );
|
|
return;
|
|
}
|
|
|
|
if ( sbase->image_type != it_rgba ) {
|
|
fprintf( stderr, "Bad call to GImageBlendOver\n" );
|
|
return;
|
|
}
|
|
|
|
for ( i=0; i<from->height; ++i ) {
|
|
dpt = (uint32 *) (dbase->data + (i+y)*dbase->bytes_per_line + x*sizeof(uint32));
|
|
spt = (uint32 *) (sbase->data + (i+from->y)*sbase->bytes_per_line + from->x*sizeof(uint32));
|
|
|
|
for (j=0; j<from->width; j++) {
|
|
a = COLOR_ALPHA(*spt);
|
|
r = ((255-a)*COLOR_RED(*dpt) + a*COLOR_RED(*spt))/255;
|
|
g = ((255-a)*COLOR_GREEN(*dpt)+ a*COLOR_GREEN(*spt))/255;
|
|
b = ((255-a)*COLOR_BLUE(*dpt) + a*COLOR_BLUE(*spt))/255;
|
|
spt++;
|
|
*dpt++ = 0xff000000 | COLOR_CREATE(r,g,b);
|
|
}
|
|
}
|
|
}
|
|
|
|
int GImageGetWidth(GImage *img) {
|
|
if ( img->list_len==0 ) {
|
|
return( img->u.image->width );
|
|
} else {
|
|
return( img->u.images[0]->width );
|
|
}
|
|
}
|
|
|
|
int GImageGetHeight(GImage *img) {
|
|
if ( img->list_len==0 ) {
|
|
return( img->u.image->height );
|
|
} else {
|
|
return( img->u.images[0]->height );
|
|
}
|
|
}
|
|
|
|
void *GImageGetUserData(GImage *img) {
|
|
return( img->userdata );
|
|
}
|
|
|
|
void GImageSetUserData(GImage *img,void *userdata) {
|
|
img->userdata = userdata;
|
|
}
|
|
|
|
static Color _GImageGetPixelRGBA(struct _GImage *base,int x, int y) {
|
|
Color val;
|
|
|
|
if ( base->image_type==it_rgba ) {
|
|
val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
|
|
return( val==base->trans?(val&0xffffff):val );
|
|
} else if ( base->image_type==it_true ) {
|
|
val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
|
|
return( val==base->trans?(val&0xffffff):(val|0xff000000) );
|
|
} else if ( base->image_type==it_index ) {
|
|
int pixel = ((uint8*) (base->data + y*base->bytes_per_line))[x];
|
|
val = base->clut->clut[pixel];
|
|
return( pixel==base->trans?(val&0xffffff):(val|0xff000000) );
|
|
} else {
|
|
int pixel = (((uint8*) (base->data + y*base->bytes_per_line))[x>>3]&(1<<(7-(x&7))) )?1:0;
|
|
if ( base->clut==NULL ) {
|
|
if ( pixel )
|
|
val = COLOR_CREATE(0xff,0xff,0xff);
|
|
else
|
|
val = COLOR_CREATE(0,0,0);
|
|
} else
|
|
val = base->clut->clut[pixel];
|
|
return( pixel==base->trans?(val&0xffffff):(val|0xff000000) );
|
|
}
|
|
}
|
|
|
|
Color GImageGetPixelRGBA(GImage *image,int x, int y) {
|
|
struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
|
|
return( _GImageGetPixelRGBA(base,x,y));
|
|
}
|
|
|
|
/* This routine is now obsolete. I include it for compatability. See GImageGetPixelRGBA */
|
|
Color _GImageGetPixelColor(struct _GImage *base,int x, int y) {
|
|
Color val;
|
|
|
|
if ( base->image_type==it_rgba ) {
|
|
val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
|
|
return( val==base->trans?~val:val );
|
|
} else if ( base->image_type==it_true ) {
|
|
val = ((uint32*) (base->data + y*base->bytes_per_line))[x] ;
|
|
return( val==base->trans?~val:val );
|
|
} else if ( base->image_type==it_index ) {
|
|
int pixel = ((uint8*) (base->data + y*base->bytes_per_line))[x];
|
|
val = base->clut->clut[pixel];
|
|
return( pixel==base->trans?~val:val );
|
|
} else {
|
|
int pixel = (((uint8*) (base->data + y*base->bytes_per_line))[x>>3]&(1<<(7-(x&7))) )?1:0;
|
|
if ( base->clut==NULL ) {
|
|
if ( pixel )
|
|
val = COLOR_CREATE(0xff,0xff,0xff);
|
|
else
|
|
val = COLOR_CREATE(0,0,0);
|
|
} else
|
|
val = base->clut->clut[pixel];
|
|
return( pixel==base->trans?~val:val );
|
|
}
|
|
}
|
|
|
|
Color GImageGetPixelColor(GImage *image,int x, int y) {
|
|
struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
|
|
return( _GImageGetPixelColor(base,x,y));
|
|
}
|