📄 image.cpp
字号:
/*
* Copyright (c) 2001,2002,2003 Mike Matsnev. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. 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.
* 3. Absolutely no warranty of function or purpose is made by the author
* Mike Matsnev.
*
* 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.
*
* $Id: Image.cpp,v 1.12.2.7 2003/09/21 12:07:35 mike Exp $
*
*/
// XXX handle out of memory exceptions gracefully
#ifdef STANDALONE
#include <windows.h>
#else
#include <afxwin.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <math.h>
extern "C" {
#include <jpeglib.h>
#include <jerror.h>
#include <png.h>
};
#include "ptr.h"
#include "Image.h"
#ifndef STANDALONE
#include "config.h"
#include "TextViewNG.h"
#endif
#define INBUF_SIZE 4096
// supported bitmap formats
enum {
BF_UNKNOWN,
BF_565,
BF_555,
BF_888
};
struct GXDisplayProperties {
DWORD cxWidth;
DWORD cyHeight; // notice lack of 'th' in the word height.
long cbxPitch; // number of bytes to move right one x pixel - can be negative.
long cbyPitch; // number of bytes to move down one y pixel - can be negative.
long cBPP; // # of bits in each pixel
DWORD ffFormat; // format flags.
};
#define kfDirect555 0x40 // 5 bits each for red, green and blue values in a pixel.
#define kfDirect565 0x80 // 5 red bits, 6 green bits and 5 blue bits per pixel
#define kfDirect888 0x100 // 8 bits each for red, green and blue values in a pixel.
#define kfDirect444 0x200 // 4 red, 4 green, 4 blue
typedef unsigned char u8;
typedef unsigned int u32;
typedef signed int s32;
typedef unsigned short u16;
static u8 gamma_table[256];
static void set_gamma(double image_gamma) {
double disp_gamma=
#ifdef STANDALONE
1.8;
#else
CTVApp::GetInt(_T("Gamma"),DEF_GAMMA)/1000000.0;
#endif
if (disp_gamma<1)
disp_gamma=1;
if (disp_gamma>5)
disp_gamma=5;
double g=1/(image_gamma*disp_gamma);
int i;
for (i=0;i<256;++i)
gamma_table[i]=((int)(pow(i/256.0,g)*256.0));
}
// returns resized line with pixels scaled by inw
static void resize_line_plain(u8 *in,u32 inw,u32 *out,u32 outw,u8 * /* bg */) {
u32 inptr=0; // input pixel position inside the output pixel
u32 outptr=inw;
u8 *top=in+inw*3; // pixel after the last
out[0]=out[1]=out[2]=0; // initialize first output pixel
do { // for each input pixel
if (inptr+outw>outptr) { // input pixel crosses two output pixels
// red
out[0] += in[0] * (outptr - inptr); // add to current pixel
out[3] = in[0] * (outw - (outptr - inptr)); // and initialize the next one
// green
out[1] += in[1] * (outptr - inptr); // add to current pixel
out[4] = in[1] * (outw - (outptr - inptr)); // and initialize the next one
// blue
out[2] += in[2] * (outptr - inptr); // add to current pixel
out[5] = in[2] * (outw - (outptr - inptr)); // and initialize the next one
// advance to next pixel
out += 3;
outptr += inw;
} else { // completely contained
out[0] += in[0] * outw;
out[1] += in[1] * outw;
out[2] += in[2] * outw;
}
in+=3; // next input pixel
inptr+=outw;
} while (in<top);
}
// returns resized line with pixels scaled by inw
static void resize_line_alpha(u8 *in,u32 inw,u32 *out,u32 outw,u8 *bg) {
u32 inptr=0; // input pixel position inside the output pixel
u32 outptr=inw;
u8 *top=in+inw*4; // pixel after the last
u8 pval; // current pixel value
u8 transp;
u8 opacity;
out[0]=out[1]=out[2]=0; // initialize first output pixel
do { // for each input pixel
opacity = in[3];
transp = 255 - in[3];
if (inptr+outw>outptr) { // input pixel crosses two output pixels
// red
pval = (in[0]*opacity + bg[0]*transp)>>8;
out[0] += pval * (outptr - inptr); // add to current pixel
out[3] = pval * (outw - (outptr - inptr)); // and initialize the next one
// green
pval = (in[1]*opacity + bg[1]*transp)>>8;
out[1] += pval * (outptr - inptr); // add to current pixel
out[4] = pval * (outw - (outptr - inptr)); // and initialize the next one
// blue
pval = (in[2]*opacity + bg[2]*transp)>>8;
out[2] += pval * (outptr - inptr); // add to current pixel
out[5] = pval * (outw - (outptr - inptr)); // and initialize the next one
// advance to next pixel
out += 3;
outptr += inw;
} else { // completely contained
out[0] += ((in[0]*opacity + bg[0]*transp) * outw) >> 8;
out[1] += ((in[1]*opacity + bg[1]*transp) * outw) >> 8;
out[2] += ((in[2]*opacity + bg[2]*transp) * outw) >> 8;
}
in+=4; // next input pixel
inptr+=outw;
} while (in<top);
}
struct imagestore {
HBITMAP bmp;
u8 *bits;
int hstep;
int vstep;
u32 width;
u32 height;
u32 realwidth;
u32 realheight;
int bmformat;
void (*packbits)(imagestore *is,u8* src);
};
struct resize_state {
u32 *accum;
u32 *lb;
s32 *error;
s32 *nexterror;
u32 inptr;
u32 outptr;
u8 *dest;
u32 inw,inh;
u32 outw,outh;
u32 outscale;
u32 inscale;
s32 max;
u32 mask[3];
u32 *membuf;
int odd;
u8 bg[4]; // background color for transparent images
// we store a function pointer here to handle transparency properly
void (*resize_line)(u8 *in,u32 inw,u32 *out,u32 outw,u8 *bg);
imagestore *output;
};
static void packbits_24_to_15(imagestore *is,u8 *src) {
u32 i;
u8 *bits=is->bits;
s32 hstep=is->hstep;
u32 width=is->width;
for (i=0;i<width;++i) {
*(u16*)bits=((u16)(gamma_table[src[0]]&0xF8)<<7)|
((u16)(gamma_table[src[1]]&0xF8)<<2)|
(gamma_table[src[2]]>>3);
src+=3;
bits+=hstep;
}
is->bits+=is->vstep;
}
static void packbits_24_to_16(imagestore *is,u8 *src) {
u32 i;
u8 *bits=is->bits;
s32 hstep=is->hstep;
u32 width=is->width;
for (i=0;i<width;++i) {
*(u16*)bits=((u16)(gamma_table[src[0]]&0xF8)<<8)|
((u16)(gamma_table[src[1]]&0xFC)<<3)|
(gamma_table[src[2]]>>3);
src+=3;
bits+=hstep;
}
is->bits+=is->vstep;
}
static void packbits_24_to_24(imagestore *is,u8 *src) {
u32 i;
u8 *bits=is->bits;
s32 hstep=is->hstep;
u32 width=is->width;
for (i=0;i<width;i++) {
bits[0]=gamma_table[src[2]];
bits[1]=gamma_table[src[1]];
bits[2]=gamma_table[src[0]];
src+=3;
bits+=hstep;
}
is->bits+=is->vstep;
}
static bool resize_state_init(resize_state *rs,u32 iinw,u32 iinh,imagestore *iout)
{
rs->output=iout;
rs->inw=iinw;
rs->inh=iinh;
rs->outw=iout->width;
rs->outh=iout->height;
u32 *p=(u32*)malloc(sizeof(u32)*13*(rs->outw+2));
if (!p)
return false;
memset(p,0,sizeof(u32)*(6*rs->outw+6*(rs->outw+2)+rs->outw));
rs->membuf=p;
rs->odd=0;
switch (iout->bmformat) {
case BF_555:
rs->mask[0]=rs->mask[1]=rs->mask[2]=0xfffffff8;
break;
case BF_565:
rs->mask[0]=0xfffffff8;
rs->mask[1]=0xfffffffc;
rs->mask[2]=0xfffffff8;
break;
case BF_888:
rs->mask[0]=rs->mask[1]=rs->mask[2]=0xffffffff;
break;
#ifndef STANDALONE
default:
ASSERT(0);
#endif
}
rs->inptr=0;
rs->outptr=rs->inh;
rs->accum=p; p+=3*(rs->outw+2);
rs->lb=p; p+=3*(rs->outw+2);
rs->nexterror=(s32*)p+3; p+=3*(rs->outw+2);
rs->error=(s32*)p+3; p+=3*(rs->outw+2);
rs->dest=(u8*)p; p+=rs->outw;
rs->outscale=(u32)(4294967296.0/(rs->inw*rs->inh));
rs->inscale=rs->inw*rs->inh;
rs->max=rs->inscale*256-1;
rs->resize_line=resize_line_plain;
return true;
}
static void resize_set_alpha(resize_state *rs,COLORREF bg) {
rs->resize_line=resize_line_alpha;
rs->bg[0]=GetRValue(bg);
rs->bg[1]=GetGValue(bg);
rs->bg[2]=GetBValue(bg);
}
static void resize_state_destroy(resize_state *rs) {
if (rs) {
free(rs->membuf);
free(rs);
}
}
static resize_state *resize_state_create(u32 iinw,u32 iinh,imagestore *out) {
resize_state *rs=(resize_state *)malloc(sizeof(resize_state));
if (rs && resize_state_init(rs,iinw,iinh,out))
return rs;
free(rs);
return NULL;
}
__inline s32 CLIP(s32 v,s32 m) {
return v<0 ? 0 : v>m ? m : v;
}
#define CLIPADD(rs,i,n) CLIP(rs->lb[i+n]+rs->error[i+n],rs->max)
#define M64(a,b,m) ((unsigned int)(((__int64)(a)*(b))>>32)&(m))
#define PIXEL_EVEN(rs,i,c) \
u = CLIPADD(rs,i,c); \
v = M64(u,rs->outscale,rs->mask[c]); \
e = u - v*rs->inscale; \
rs->error[i+3+c] += (e*7)>>4; \
rs->nexterror[i-3+c] += (e*3)>>4; \
rs->nexterror[i+0+c] += (e*5)>>4; \
rs->nexterror[i+3+c] += e>>4; \
rs->dest[i+c] = v;
#define PIXEL_ODD(rs,i,c) \
u = CLIPADD(rs,i,c); \
v = M64(u,rs->outscale,rs->mask[c]); \
e = u-v*rs->inscale; \
rs->error[i-3+c] += (e*7)>>4; \
rs->nexterror[i+3+c] += (e*3)>>4; \
rs->nexterror[i+0+c] += (e*5)>>4; \
rs->nexterror[i-3+c] += e>>4; \
rs->dest[i+c]=v;
// dither lb into dest, left to right
void resize_dither_even(struct resize_state *rs) {
u32 i;
s32 u,v,e;
for (i=0;i<3*rs->outw;i+=3) {
PIXEL_EVEN(rs,i,0);
PIXEL_EVEN(rs,i,1);
PIXEL_EVEN(rs,i,2);
}
}
// dither lb into dest, right to left
void resize_dither_odd(struct resize_state *rs) {
s32 i;
s32 u,v,e;
for (i=3*rs->outw-3;i>=0;i-=3) {
PIXEL_ODD(rs,i,0);
PIXEL_ODD(rs,i,1);
PIXEL_ODD(rs,i,2);
}
}
// dither lb into dest
void resize_dither(struct resize_state *rs) {
s32 *tmp;
if (rs->odd)
resize_dither_odd(rs);
else
resize_dither_even(rs);
tmp=rs->error;
rs->error=rs->nexterror;
rs->nexterror=tmp;
memset(tmp,0,sizeof(s32)*3*rs->outw);
rs->odd=!rs->odd;
rs->output->packbits(rs->output,rs->dest);
}
void resize_add_line(struct resize_state *rs,u8 *line) {
u32 i;
// resize the line horizontally
rs->resize_line(line,rs->inw,rs->lb,rs->outw,rs->bg);
// perform one loop iteration
if (rs->inptr+rs->outh>=rs->outptr) {
u32 c1=rs->outptr-rs->inptr;
u32 c2=rs->outh-c1;
u32 v;
for (i=0;i<3*rs->outw;i+=3) {
v = rs->accum[i+0]+rs->lb[i+0]*c1;
rs->accum[i+0] = rs->lb[i+0]*c2;
rs->lb[i+0] = v;
v = rs->accum[i+1]+rs->lb[i+1]*c1;
rs->accum[i+1] = rs->lb[i+1]*c2;
rs->lb[i+1] = v;
v = rs->accum[i+2]+rs->lb[i+2]*c1;
rs->accum[i+2] = rs->lb[i+2]*c2;
rs->lb[i+2] = v;
}
resize_dither(rs);
rs->outptr+=rs->inh;
} else {
for (i=0;i<3*rs->outw;i+=3) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -