This commit is contained in:
nothings.org 2008-10-20 06:20:45 +00:00
parent 33491d6a7e
commit eb8babe4db
4 changed files with 162 additions and 24 deletions

75
imv.c
View File

@ -53,6 +53,8 @@
#define ALLOW_RECOLORING 1
#endif
//#define MONO2
// implement USE_STBI
#if USE_STBI
@ -388,8 +390,12 @@ void make_image(Image *z, int image_x, int image_y, uint8 *image_data, BOOL imag
#if ALLOW_RECOLORING
if (mono) {
#ifdef MONO2
int p = image_data[k+2];
#else
int y = image_data[k+0]*5 + image_data[k+1]*9 + image_data[k+2]*2;
int p = (int) stb_linear_remap(y, ymin, ymax, 0,255);
#endif
image_data[k+0] = p;
image_data[k+1] = p;
image_data[k+2] = p;
@ -641,6 +647,7 @@ char helptext_left[] =
" CTRL-B: toggle white stripe in border\n"
" L: toggle filename label\n"
" S: slideshow in current directory\n"
" CTRL-S: sharpen when upscaling\n"
;
char helptext_right[] =
@ -957,6 +964,8 @@ void GetAdjustedWindowRect(HWND win, RECT *rect)
}
}
int allow_fullsize;
// compute the size we'd prefer this window to be at for 1:1-ness
void ideal_window_size(int w, int h, int *w_ideal, int *h_ideal, int *x, int *y)
{
@ -967,7 +976,7 @@ void ideal_window_size(int w, int h, int *w_ideal, int *h_ideal, int *x, int *y)
int cx2 = GetSystemMetrics(SM_CXSCREEN);
int cy2 = GetSystemMetrics(SM_CYSCREEN);
if (w <= cx2 && h <= cy2) {
if (allow_fullsize || (w <= cx2 && h <= cy2)) {
// if the image fits on the primary monitor, go for it
*w_ideal = w;
*h_ideal = h;
@ -1193,10 +1202,11 @@ void free_fileinfo(void)
// food.jpg
// use upper, not lower, to get better sorting versus '_'
// no, use lower not upper, to get sorting that matches explorer
__forceinline char tupper(char b)
{
//if (b >= 'A' && b <= 'Z') return b - 'A' + 'a';
if (b >= 'a' && b <= 'z') return b - 'a' + 'A';
if (b >= 'A' && b <= 'Z') return b - 'A' + 'a';
//if (b >= 'a' && b <= 'z') return b - 'a' + 'A';
return b;
}
@ -2004,7 +2014,7 @@ BOOL CALLBACK PrefDlgProc(HWND hdlg, UINT imsg, WPARAM wparam, LPARAM lparam)
#else
int channels;
Bool loaded_as_rgb;
uint8 *data = imv_decode_from_memory(rom_images[n], 2000, &x, &y, &loaded_as_rgb, &channels, BPP);
uint8 *data = imv_decode_from_memory(rom_images[n], 2000, &x, &y, &loaded_as_rgb, &channels, BPP, "");
assert(channels == BPP);
pref_image = bmp_alloc(x, y);
pref_image->pixels = data;
@ -2169,9 +2179,11 @@ void performance_test(void)
#define WM_APPCOMMAND 0x0319
#define APPCOMMAND_BROWSER_BACKWARD 1
#define APPCOMMAND_BROWSER_FORWARD 2
#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
#endif
#ifndef APPCOMMAND_OPEN
#define APPCOMMAND_OPEN 30
#define FAPPCOMMAND_MASK 0xF000
#define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
#endif
// ok, surely windows doesn't BY DESIGN require you to store your
@ -2179,6 +2191,7 @@ void performance_test(void)
// or some such to tell you what instance a thread came from. But the
// HINSTANCE is needed to launch the preferences dialog. Oh well!
HINSTANCE inst;
int sharpen=0;
int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@ -2503,11 +2516,26 @@ int WINAPI MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
resize(-1);
break;
case 'S' | MY_CTRL:
sharpen = !sharpen;
--cur->y;
--cur->x;
size_to_current(0);
break;
case 'O':
case MY_CTRL | 'O':
open_file();
break;
case MY_CTRL | 'F':
allow_fullsize = !allow_fullsize;
if (allow_fullsize)
display_mode = DISPLAY_current;
else
display_mode = !display_mode;
toggle_display();
break;
case MY_ALT | '\r':
toggle_display();
break;
@ -3052,7 +3080,7 @@ typedef uint32 Color;
// lerp() is just blend() that also "blends" alpha
// put a/256 of src over dest, including alpha
// again, cannot be used for a=256
;// again, cannot be used for a=256
static Color lerp(Color dest, Color src, uint8 a)
{
int rb_src = src & 0xff00ff;
@ -3413,6 +3441,36 @@ Image *downsample_two_thirds(Image *src)
return res;
}
void do_sharpen(uint8 *data, int stride, int x, int y)
{
int i,j,k;
uint8 prev[3200*4];
if (x > 3200) return;
memcpy(prev, data, x*BPP);
for (j=1; j < y-1; ++j) {
uint8 *next = data + stride*(j+1);
unsigned char left[4];
memcpy(left, data+stride*j+BPP, BPP);
for (i=1; i < x-1; ++i) {
unsigned char temp[4];
memcpy(temp, data+stride*j+i*BPP,BPP);
for (k=0; k < BPP; ++k) {
int v = data[stride*j+i*BPP+k] * 16;
v -= (prev[i*BPP+k-4] + prev[i*BPP+k] + prev[i*BPP+k+4]);
v -= (next[i*BPP+k-4] + next[i*BPP+k] + next[i*BPP+k+4]);
v -= (left[k] + data[stride*j+i*BPP+4+k]);
v >>= 3;
if (v < 0) v = 0; else if (v > 255) v = 255;
data[stride*j+i*BPP+k] = v;
}
// now temp becomes the new left
// but first:
memcpy(prev+(i-1)*BPP, left, BPP);
memcpy(left, temp, BPP);
}
}
}
Image *grScaleBitmap(Image *src, int gx, int gy, Image *dest)
{
Image *to_free, *res;
@ -3423,6 +3481,9 @@ Image *grScaleBitmap(Image *src, int gx, int gy, Image *dest)
if (gx > src->x || gy > src->y) {
upsample = TRUE;
} else {
// current biggest problem perf-wise is on scaling down, we don't
// use threads
// maybe should do something smarter here, like find the
// nearest box size, instead of repetitive powers of two
while (gx <= (src->x >> 1) && gy <= (src->y >> 1)) {
@ -3465,6 +3526,8 @@ Image *grScaleBitmap(Image *src, int gx, int gy, Image *dest)
imfree(to_free);
#endif
}
if (upsample && sharpen)
do_sharpen(res->pixels, res->stride, res->x, res->y);
return res;
}
#endif // BPP==4

View File

@ -1,3 +1,8 @@
Version 1.0: Release 1
* feature: open a directory
* feature: sharpen when upscaling
* secret feature: toggle use of full-size virtual desktop
Version 0.991: Beta 12
* bugfix: .spk file recursion issues, security

View File

@ -1,4 +1,4 @@
/* stbi-1.15 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
/* stbi-1.17 - public domain JPEG/PNG reader - http://nothings.org/stb_image.c
when you control the images you're loading
QUICK NOTES:
@ -6,7 +6,7 @@
avoid problematic images and only need the trivial interface
JPEG baseline (no JPEG progressive, no oddball channel decimations)
PNG non-interlaced
PNG 8-bit only
BMP non-1bpp, non-RLE
TGA (not sure what subset, if a subset)
PSD (composited view only, no extra channels)
@ -19,6 +19,8 @@
stbi_info_*
history:
1.17 support interlaced PNG
1.16 major bugfix - convert_format converted one too many pixels
1.15 initialize some fields for thread safety
1.14 fix threadsafe conversion bug; header-file-only version (#define STBI_HEADER_FILE_ONLY before including)
1.13 threadsafe
@ -1388,7 +1390,7 @@ static int process_marker(jpeg *z, int m)
z->dequant[t][dezigzag[i]] = get8u(&z->s);
#if STBI_SIMD
for (i=0; i < 64; ++i)
z->dequant2[t][i] = dequant[t][i];
z->dequant2[t][i] = z->dequant[t][i];
#endif
L -= 65;
}
@ -1654,7 +1656,7 @@ static uint8 *resample_row_generic(uint8 *out, uint8 *in_near, uint8 *in_far, in
// 0.38 seconds on 3*anemones.jpg (0.25 with processor = Pro)
// VC6 without processor=Pro is generating multiple LEAs per multiply!
static void YCbCr_to_RGB_row(uint8 *out, uint8 *y, uint8 *pcb, uint8 *pcr, int count, int step)
static void YCbCr_to_RGB_row(uint8 *out, const uint8 *y, const uint8 *pcb, const uint8 *pcr, int count, int step)
{
int i;
for (i=0; i < count; ++i) {
@ -2185,6 +2187,7 @@ static void init_defaults(void)
for (i=0; i <= 31; ++i) default_distance[i] = 5;
}
int stbi_png_partial; // a quick hack to only allow decoding some of a PNG... I should implement real streaming support instead
static int parse_zlib(zbuf *a, int parse_header)
{
int final, type;
@ -2210,6 +2213,8 @@ static int parse_zlib(zbuf *a, int parse_header)
}
if (!parse_huffman_block(a)) return 0;
}
if (stbi_png_partial && a->zout - a->zout_start > 65536)
break;
} while (!final);
return 1;
}
@ -2348,17 +2353,23 @@ static int paeth(int a, int b, int c)
}
// create the png data from post-deflated data
static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n)
static int create_png_image_raw(png *a, uint8 *raw, uint32 raw_len, int out_n, uint32 x, uint32 y)
{
stbi *s = &a->s;
uint32 i,j,stride = s->img_x*out_n;
uint32 i,j,stride = x*out_n;
int k;
int img_n = s->img_n; // copy it into a local for later
assert(out_n == s->img_n || out_n == s->img_n+1);
a->out = (uint8 *) malloc(s->img_x * s->img_y * out_n);
if (stbi_png_partial) y = 1;
a->out = (uint8 *) malloc(x * y * out_n);
if (!a->out) return e("outofmem", "Out of memory");
if (raw_len != (img_n * s->img_x + 1) * s->img_y) return e("not enough pixels","Corrupt PNG");
for (j=0; j < s->img_y; ++j) {
if (!stbi_png_partial) {
if (s->img_x == x && s->img_y == y)
if (raw_len != (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG");
else // interlaced:
if (raw_len < (img_n * x + 1) * y) return e("not enough pixels","Corrupt PNG");
}
for (j=0; j < y; ++j) {
uint8 *cur = a->out + stride*j;
uint8 *prior = cur - stride;
int filter = *raw++;
@ -2385,7 +2396,7 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n)
if (img_n == out_n) {
#define CASE(f) \
case f: \
for (i=s->img_x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
for (i=x-1; i >= 1; --i, raw+=img_n,cur+=img_n,prior+=img_n) \
for (k=0; k < img_n; ++k)
switch(filter) {
CASE(F_none) cur[k] = raw[k]; break;
@ -2401,7 +2412,7 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n)
assert(img_n+1 == out_n);
#define CASE(f) \
case f: \
for (i=s->img_x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \
for (k=0; k < img_n; ++k)
switch(filter) {
CASE(F_none) cur[k] = raw[k]; break;
@ -2418,6 +2429,47 @@ static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n)
return 1;
}
static int create_png_image(png *a, uint8 *raw, uint32 raw_len, int out_n, int interlaced)
{
uint8 *final;
int p;
int save;
if (!interlaced)
return create_png_image_raw(a, raw, raw_len, out_n, a->s.img_x, a->s.img_y);
save = stbi_png_partial;
stbi_png_partial = 0;
// deinterlacing
final = malloc(a->s.img_x * a->s.img_y * out_n);
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
int xspc[] = { 8,8,4,4,2,2,1 };
int yspc[] = { 8,8,8,4,4,2,2 };
int i,j,x,y;
// pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
x = (a->s.img_x - xorig[p] + xspc[p]-1) / xspc[p];
y = (a->s.img_y - yorig[p] + yspc[p]-1) / yspc[p];
if (x && y) {
if (!create_png_image_raw(a, raw, raw_len, out_n, x, y)) {
free(final);
return 0;
}
for (j=0; j < y; ++j)
for (i=0; i < x; ++i)
memcpy(final + (j*yspc[p]+yorig[p])*a->s.img_x*out_n + (i*xspc[p]+xorig[p])*out_n,
a->out + (j*x+i)*out_n, out_n);
free(a->out);
raw += (x*out_n+1)*y;
raw_len -= (x*out_n+1)*y;
}
}
a->out = final;
stbi_png_partial = save;
return 1;
}
static int compute_transparency(png *z, uint8 tc[3], int out_n)
{
stbi *s = &z->s;
@ -2482,7 +2534,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
uint8 palette[1024], pal_img_n=0;
uint8 has_trans=0, tc[3];
uint32 ioff=0, idata_limit=0, i, pal_len=0;
int first=1,k;
int first=1,k,interlace=0;
stbi *s = &z->s;
if (!check_png_header(s)) return 0;
@ -2495,7 +2547,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
return e("first not IHDR","Corrupt PNG");
switch (c.type) {
case PNG_TYPE('I','H','D','R'): {
int depth,color,interlace,comp,filter;
int depth,color,comp,filter;
if (!first) return e("multiple IHDR","Corrupt PNG");
if (c.length != 13) return e("bad IHDR len","Corrupt PNG");
s->img_x = get32(s); if (s->img_x > (1 << 24)) return e("too large","Very large image (corrupt?)");
@ -2505,7 +2557,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
if (color == 3) pal_img_n = 3; else if (color & 1) return e("bad ctype","Corrupt PNG");
comp = get8(s); if (comp) return e("bad comp method","Corrupt PNG");
filter= get8(s); if (filter) return e("bad filter method","Corrupt PNG");
interlace = get8(s); if (interlace) return e("interlaced","PNG not supported: interlaced mode");
interlace = get8(s); if (interlace>1) return e("bad interlace method","Corrupt PNG");
if (!s->img_x || !s->img_y) return e("0-pixel image","Corrupt PNG");
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
@ -2590,7 +2642,7 @@ static int parse_png_file(png *z, int scan, int req_comp)
s->img_out_n = s->img_n+1;
else
s->img_out_n = s->img_n;
if (!create_png_image(z, z->expanded, raw_len, s->img_out_n)) return 0;
if (!create_png_image(z, z->expanded, raw_len, s->img_out_n, interlace)) return 0;
if (has_trans)
if (!compute_transparency(z, tc, s->img_out_n)) return 0;
if (pal_img_n) {
@ -2700,7 +2752,23 @@ int stbi_png_test_memory(stbi_uc const *buffer, int len)
// TODO: load header from png
#ifndef STBI_NO_STDIO
extern int stbi_png_info (char const *filename, int *x, int *y, int *comp);
int stbi_png_info (char const *filename, int *x, int *y, int *comp)
{
png p;
FILE *f = fopen(filename, "rb");
if (!f) return 0;
start_file(&p.s, f);
if (parse_png_file(&p, SCAN_header, 0)) {
if(x) *x = p.s.img_x;
if(y) *y = p.s.img_y;
if (comp) *comp = p.s.img_n;
fclose(f);
return 1;
}
fclose(f);
return 0;
}
extern int stbi_png_info_from_file (FILE *f, int *x, int *y, int *comp);
#endif
extern int stbi_png_info_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp);
@ -2783,7 +2851,7 @@ static int shiftsigned(int v, int shift, int bits)
static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{
uint8 *out;
unsigned int mr=0,mg=0,mb=0,ma=0;
unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0;
stbi_uc pal[256][4];
int psize=0,i,j,compress=0,width;
int bpp, flip_vertically, pad, target, offset, hsz;
@ -2832,6 +2900,8 @@ static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
mr = 0xff << 16;
mg = 0xff << 8;
mb = 0xff << 0;
ma = 0xff << 24;
fake_a = 1; // @TODO: check for cases like alpha value is all 0 and switch it to 255
} else {
mr = 31 << 10;
mg = 31 << 5;

View File

@ -1 +1 @@
set VERSION="0.99"
set VERSION="1.0"