diff --git a/LumaBmp2NinJpg/.gitignore b/LumaBmp2NinJpg/.gitignore new file mode 100644 index 0000000..3c4bb72 --- /dev/null +++ b/LumaBmp2NinJpg/.gitignore @@ -0,0 +1,3 @@ +/build +*.elf +*.exe diff --git a/LumaBmp2NinJpg/LumaBmp2NinJpg.3dsx b/LumaBmp2NinJpg/LumaBmp2NinJpg.3dsx new file mode 100644 index 0000000..c77d068 Binary files /dev/null and b/LumaBmp2NinJpg/LumaBmp2NinJpg.3dsx differ diff --git a/LumaBmp2NinJpg/LumaBmp2NinJpg.smdh b/LumaBmp2NinJpg/LumaBmp2NinJpg.smdh new file mode 100644 index 0000000..0fc6351 Binary files /dev/null and b/LumaBmp2NinJpg/LumaBmp2NinJpg.smdh differ diff --git a/LumaBmp2NinJpg/Makefile b/LumaBmp2NinJpg/Makefile new file mode 100644 index 0000000..632882b --- /dev/null +++ b/LumaBmp2NinJpg/Makefile @@ -0,0 +1,229 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# GRAPHICS is a list of directories containing graphics files +# GFXBUILD is the directory where converted graphics files will be placed +# If set to $(BUILD), it will statically link in the converted +# files as if they were data files. +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include +GRAPHICS := gfx +GFXBUILD := $(BUILD) +#ROMFS := romfs +#GFXBUILD := $(ROMFS)/gfx + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft + +CFLAGS := -g -Wall -O2 -mword-relocations \ + -ffunction-sections \ + $(ARCH) + +CFLAGS += $(INCLUDE) -D__3DS__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) +GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +ifeq ($(GFXBUILD),$(BUILD)) +#--------------------------------------------------------------------------------- +export T3XFILES := $(GFXFILES:.t3s=.t3x) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- +export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) +export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ + $(addsuffix .o,$(T3XFILES)) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ + $(addsuffix .h,$(subst .,_,$(BINFILES))) \ + $(GFXFILES:.t3s=.h) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + +.PHONY: all clean + +#--------------------------------------------------------------------------------- +all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +$(BUILD): + @mkdir -p $@ + +ifneq ($(GFXBUILD),$(BUILD)) +$(GFXBUILD): + @mkdir -p $@ +endif + +ifneq ($(DEPSDIR),$(BUILD)) +$(DEPSDIR): + @mkdir -p $@ +endif + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) + +#--------------------------------------------------------------------------------- +$(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) + +$(OFILES_SOURCES) : $(HFILES) + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +.PRECIOUS : %.t3x %.shbin +#--------------------------------------------------------------------------------- +%.t3x.o %_t3x.h : %.t3x +#--------------------------------------------------------------------------------- + $(SILENTMSG) $(notdir $<) + $(bin2o) + +#--------------------------------------------------------------------------------- +%.shbin.o %_shbin.h : %.shbin +#--------------------------------------------------------------------------------- + $(SILENTMSG) $(notdir $<) + $(bin2o) + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/LumaBmp2NinJpg/source/cio.c b/LumaBmp2NinJpg/source/cio.c new file mode 100644 index 0000000..b5fb939 --- /dev/null +++ b/LumaBmp2NinJpg/source/cio.c @@ -0,0 +1,175 @@ +/* Copyright (C) 2009 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + + + +#include +#include "cjpeg.h" +#include "cio.h" + + +/* + * flush input and output of compress IO. + */ + +bool +flush_cin_buffer(void *cio) +{ + mem_mgr *in = ((compress_io *) cio)->in; + size_t len = in->end - in->set; + memset(in->set, 0, len); + if (fread(in->set, sizeof(UINT8), len, in->fp) != len) + return false; +#ifdef REVERSED + fseek(in->fp, -len * 2, SEEK_CUR); +#endif + in->pos = in->set; + return true; +} + +bool +flush_cout_buffer(void *cio) +{ + mem_mgr *out = ((compress_io *) cio)->out; + size_t len = out->pos - out->set; + if (fwrite(out->set, sizeof(UINT8), len, out->fp) != len) + return false; + memset(out->set, 0, len); + out->pos = out->set; + return true; +} + + +/* + * init memory manager. + */ + +void +init_mem(compress_io *cio, + FILE *in_fp, int in_size, FILE *out_fp, int out_size) +{ + cio->in = (mem_mgr *) malloc(sizeof(mem_mgr)); + if (!cio->in) + err_exit(BUFFER_ALLOC_ERR); + cio->in->set = (UINT8 *) malloc(sizeof(UINT8) * in_size); + if (!cio->in->set) + err_exit(BUFFER_ALLOC_ERR); + cio->in->pos = cio->in->set; + cio->in->end = cio->in->set + in_size; + cio->in->flush_buffer = flush_cin_buffer; + cio->in->fp = in_fp; + + cio->out = (mem_mgr *) malloc(sizeof(mem_mgr)); + if (!cio->out) + err_exit(BUFFER_ALLOC_ERR); + cio->out->set = (UINT8 *) malloc(sizeof(UINT8) * out_size); + if (!cio->out->set) + err_exit(BUFFER_ALLOC_ERR); + cio->out->pos = cio->out->set; + cio->out->end = cio->out->set + out_size; + cio->out->flush_buffer = flush_cout_buffer; + cio->out->fp = out_fp; + + cio->temp_bits.len = 0; + cio->temp_bits.val = 0; +} + +void +free_mem(compress_io *cio) +{ + fflush(cio->out->fp); + free(cio->in->set); + free(cio->out->set); + free(cio->in); + free(cio->out); +} + + +/* + * write operations. + */ + +void +write_byte(compress_io *cio, UINT8 val) +{ + mem_mgr *out = cio->out; + *(out->pos)++ = val & 0xFF; + if (out->pos == out->end) { + if (!(out->flush_buffer)(cio)) + err_exit(BUFFER_WRITE_ERR); + } +} + +void +write_word(compress_io *cio, UINT16 val) +{ + write_byte(cio, (val >> 8) & 0xFF); + write_byte(cio, val & 0xFF); +} + +void +write_marker(compress_io *cio, JPEG_MARKER mark) +{ + write_byte(cio, 0xFF); + write_byte(cio, (int) mark); +} + +void +write_bits(compress_io *cio, BITS bits) +{ + BITS *temp = &(cio->temp_bits); + UINT16 word; + UINT8 byte1, byte2; + int len = bits.len + temp->len - 16; + if (len >= 0) { + word = temp->val | bits.val >> len; + byte1 = word >> 8; + write_byte(cio, byte1); + if (byte1 == 0xFF) + write_byte(cio, 0); + byte2 = word & 0xFF; + write_byte(cio, byte2); + if (byte2 == 0xFF) + write_byte(cio, 0); + temp->len = len; + temp->val = bits.val << (16 - len); + } + else { + temp->len = 16 + len; + temp->val |= bits.val << -len; + } +} + +void +write_align_bits(compress_io *cio) +{ + BITS *temp = &(cio->temp_bits); + BITS align_bits; + align_bits.len = 8 - temp->len % 8; + align_bits.val = (UINT16) ~0x0 >> temp->len % 8; + UINT8 byte; + write_bits(cio, align_bits); + if (temp->len == 8) { + byte = temp->val >> 8; + write_byte(cio, byte); + if (byte == 0xFF) + write_byte(cio, 0); + } +} + diff --git a/LumaBmp2NinJpg/source/cio.h b/LumaBmp2NinJpg/source/cio.h new file mode 100644 index 0000000..5e3a4c6 --- /dev/null +++ b/LumaBmp2NinJpg/source/cio.h @@ -0,0 +1,36 @@ + +#ifndef __CIO_H +#define __CIO_H + +typedef bool (*CIO_METHOD) (void *); + +typedef struct { + UINT8 *set; + UINT8 *pos; + UINT8 *end; + CIO_METHOD flush_buffer; + FILE *fp; +} mem_mgr; + +typedef struct { + mem_mgr *in; + mem_mgr *out; + BITS temp_bits; +} compress_io; + + +bool flush_cin_buffer(void *cio); +bool flush_cout_buffer(void *cio); + +void init_mem(compress_io *cio, + FILE *in_fp, int in_size, FILE *out_fp, int out_size); +void free_mem(compress_io *cio); + +void write_byte(compress_io *cio, UINT8 val); +void write_word(compress_io *cio, UINT16 val); +void write_marker(compress_io *cio, JPEG_MARKER mark); +void write_bits(compress_io *cio, BITS bits); +void write_align_bits(compress_io *cio); + +#endif /* __CIO_H */ + diff --git a/LumaBmp2NinJpg/source/cjpeg.c b/LumaBmp2NinJpg/source/cjpeg.c new file mode 100644 index 0000000..c4e5019 --- /dev/null +++ b/LumaBmp2NinJpg/source/cjpeg.c @@ -0,0 +1,384 @@ +/* Copyright (C) 2009 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + + + +#include "cjpeg.h" +#include "cio.h" + +/* YCbCr to RGB transformation */ + +/* + * precalculated tables for a faster YCbCr->RGB transformation. + * use a INT32 table because we'll scale values by 2^16 and + * work with integers. + */ + +ycbcr_tables ycc_tables; + +void +init_ycbcr_tables() +{ + UINT16 i; + for (i = 0; i < 256; i++) { + ycc_tables.r2y[i] = (INT32)(65536 * 0.299 + 0.5) * i; + ycc_tables.r2cb[i] = (INT32)(65536 * -0.16874 + 0.5) * i; + ycc_tables.r2cr[i] = (INT32)(32768) * i; + ycc_tables.g2y[i] = (INT32)(65536 * 0.587 + 0.5) * i; + ycc_tables.g2cb[i] = (INT32)(65536 * -0.33126 + 0.5) * i; + ycc_tables.g2cr[i] = (INT32)(65536 * -0.41869 + 0.5) * i; + ycc_tables.b2y[i] = (INT32)(65536 * 0.114 + 0.5) * i; + ycc_tables.b2cb[i] = (INT32)(32768) * i; + ycc_tables.b2cr[i] = (INT32)(65536 * -0.08131 + 0.5) * i; + } +} + +void +rgb_to_ycbcr(UINT8 *rgb_unit, ycbcr_unit *ycc_unit, int x, int w) +{ + ycbcr_tables *tbl = &ycc_tables; + UINT8 r, g, b; +#ifdef REVERSED + int src_pos = (x + w * (DCTSIZE - 1)) * 3; +#else + int src_pos = x * 3; +#endif + int dst_pos = 0; + int i, j; + for (j = 0; j < DCTSIZE; j++) { + for (i = 0; i < DCTSIZE; i++) { + b = rgb_unit[src_pos]; + g = rgb_unit[src_pos+1]; + r = rgb_unit[src_pos+2]; + ycc_unit->y[dst_pos] = (INT8) ((UINT8) + ((tbl->r2y[r] + tbl->g2y[g] + tbl->b2y[b]) >> 16) - 128); + ycc_unit->cb[dst_pos] = (INT8) ((UINT8) + ((tbl->r2cb[r] + tbl->g2cb[g] + tbl->b2cb[b]) >> 16)); + ycc_unit->cr[dst_pos] = (INT8) ((UINT8) + ((tbl->r2cr[r] + tbl->g2cr[g] + tbl->b2cr[b]) >> 16)); + src_pos += 3; + dst_pos++; + } +#ifdef REVERSED + src_pos -= (w + DCTSIZE) * 3; +#else + src_pos += (w - DCTSIZE) * 3; +#endif + } +} + + +/* quantization */ + +quant_tables q_tables; + +void +init_quant_tables(UINT32 scale_factor) +{ + quant_tables *tbl = &q_tables; + int temp1, temp2; + int i; + for (i = 0; i < DCTSIZE2; i++) { + temp1 = ((UINT32) STD_LU_QTABLE[i] * scale_factor + 50) / 100; + if (temp1 < 1) + temp1 = 1; + if (temp1 > 255) + temp1 = 255; + tbl->lu[ZIGZAG[i]] = (UINT8) temp1; + + temp2 = ((UINT32) STD_CH_QTABLE[i] * scale_factor + 50) / 100; + if (temp2 < 1) + temp2 = 1; + if (temp2 > 255) + temp2 = 255; + tbl->ch[ZIGZAG[i]] = (UINT8) temp2; + } +} + +void +jpeg_quant(ycbcr_unit *ycc_unit, quant_unit *q_unit) +{ + quant_tables *tbl = &q_tables; + float q_lu, q_ch; + int x, y, i = 0; + for (x = 0; x < DCTSIZE; x++) { + for (y = 0; y < DCTSIZE; y++) { + q_lu = 1.0 / ((double) tbl->lu[ZIGZAG[i]] * \ + AAN_SCALE_FACTOR[x] * AAN_SCALE_FACTOR[y] * 8.0); + q_ch = 1.0 / ((double) tbl->ch[ZIGZAG[i]] * \ + AAN_SCALE_FACTOR[x] * AAN_SCALE_FACTOR[y] * 8.0); + + q_unit->y[i] = (INT16)(ycc_unit->y[i]*q_lu + 16384.5) - 16384; + q_unit->cb[i] = (INT16)(ycc_unit->cb[i]*q_ch + 16384.5) - 16384; + q_unit->cr[i] = (INT16)(ycc_unit->cr[i]*q_ch + 16384.5) - 16384; + + i++; + } + } +} + + +/* huffman compression */ + +huff_tables h_tables; + +void +set_huff_table(UINT8 *nrcodes, UINT8 *values, BITS *h_table) +{ + int i, j, k; + j = 0; + UINT16 value = 0; + for (i = 1; i <= 16; i++) { + for (k = 0; k < nrcodes[i]; k++) { + h_table[values[j]].len = i; + h_table[values[j]].val = value; + j++; + value++; + } + value <<= 1; + } +} + +void +init_huff_tables() +{ + huff_tables *tbl = &h_tables; + set_huff_table(STD_LU_DC_NRCODES, STD_LU_DC_VALUES, tbl->lu_dc); + set_huff_table(STD_LU_AC_NRCODES, STD_LU_AC_VALUES, tbl->lu_ac); + set_huff_table(STD_CH_DC_NRCODES, STD_CH_DC_VALUES, tbl->ch_dc); + set_huff_table(STD_CH_AC_NRCODES, STD_CH_AC_VALUES, tbl->ch_ac); +} + +void +set_bits(BITS *bits, INT16 data) +{ + UINT16 pos_data; + int i; + pos_data = data < 0 ? ~data + 1 : data; + for (i = 15; i >= 0; i--) + if ((pos_data & (1 << i)) != 0) + break; + bits->len = i + 1; + bits->val = data < 0 ? data + (1 << bits->len) - 1 : data; +} + +#ifdef DEBUG +void +print_bits(BITS bits) +{ + printf("%hu %hu\t", bits.len, bits.val); +} +#endif + +/* + * compress JPEG + */ +void +jpeg_compress(compress_io *cio, + INT16 *data, INT16 *dc, BITS *dc_htable, BITS *ac_htable) +{ + INT16 zigzag_data[DCTSIZE2]; + BITS bits; + INT16 diff; + int i, j; + int zero_num; + int mark; + + /* zigzag encode */ + for (i = 0; i < DCTSIZE2; i++) + zigzag_data[ZIGZAG[i]] = data[i]; + + /* write DC */ + diff = zigzag_data[0] - *dc; + *dc = zigzag_data[0]; + + if (diff == 0) + write_bits(cio, dc_htable[0]); + else { + set_bits(&bits, diff); + write_bits(cio, dc_htable[bits.len]); + write_bits(cio, bits); + } + + /* write AC */ + int end = DCTSIZE2 - 1; + while (zigzag_data[end] == 0 && end > 0) + end--; + for (i = 1; i <= end; i++) { + j = i; + while (zigzag_data[j] == 0 && j <= end) + j++; + zero_num = j - i; + for (mark = 0; mark < zero_num / 16; mark++) + write_bits(cio, ac_htable[0xF0]); + zero_num = zero_num % 16; + set_bits(&bits, zigzag_data[j]); + write_bits(cio, ac_htable[zero_num * 16 + bits.len]); + write_bits(cio, bits); + i = j; + } + + /* write end of unit */ + if (end != DCTSIZE2 - 1) + write_bits(cio, ac_htable[0]); +} + + +/* + * main JPEG encoding + */ +void +jpeg_encode(compress_io *cio, bmp_info *binfo) +{ + /* init tables */ + UINT32 scale = 50; + init_ycbcr_tables(); + init_quant_tables(scale); + init_huff_tables(); + + /* write info */ + write_file_header(cio); + write_frame_header(cio, binfo); + write_scan_header(cio); + + /* encode */ + mem_mgr *in = cio->in; + ycbcr_unit ycc_unit; + quant_unit q_unit; + INT16 dc_y = 0, + dc_cb = 0, + dc_cr = 0; + int x, y; + +#ifdef REVERSED + int in_size = in->end - in->set; + int offset = binfo->offset + (binfo->datasize/in_size - 1) * in_size; +#else + int offset = binfo->offset; +#endif + fseek(in->fp, offset, SEEK_SET); + + for (y = 0; y < binfo->height; y += 8) { + + /* flush input buffer */ + if (! (in->flush_buffer) (cio)) + err_exit(BUFFER_READ_ERR); + + for (x = 0; x < binfo->width; x += 8) { + + /* convert RGB unit to YCbCr unit */ + rgb_to_ycbcr(in->set, &ycc_unit, x, binfo->width); + + /* forward DCT on YCbCr unit */ + jpeg_fdct(ycc_unit.y); + jpeg_fdct(ycc_unit.cb); + jpeg_fdct(ycc_unit.cr); + + /* quantization, store in quant unit */ + jpeg_quant(&ycc_unit, &q_unit); + + /* huffman compression, write */ + jpeg_compress(cio, q_unit.y, &dc_y, + h_tables.lu_dc, h_tables.lu_ac); + jpeg_compress(cio, q_unit.cb,&dc_cb, + h_tables.ch_dc, h_tables.ch_ac); + jpeg_compress(cio, q_unit.cr,&dc_cr, + h_tables.ch_dc, h_tables.ch_ac); + } + } + write_align_bits(cio); + + /* write file end */ + write_file_trailer(cio); +} + + + +bool +is_bmp(FILE *fp) +{ + UINT8 marker[3]; + if (fread(marker, sizeof(UINT16), 2, fp) != 2) + err_exit(FILE_READ_ERR); + if (marker[0] != 0x42 || marker[1] != 0x4D) + return false; + rewind(fp); + return true; +} + +void +err_exit(const char *error_string, int exit_num) +{ + printf(error_string); + exit(exit_num); +} + + +void +print_help() +{ + printf("compress BMP file into JPEG file.\n"); + printf("Usage:\n"); + printf(" cjpeg {BMP} {JPEG}\n"); + printf("\n"); + printf("Author: Yu, Le \n"); +} + + +#ifdef main_lib +int main(int argc, char *argv[]) +{ + if (argc == 3) { + /* open bmp file */ + FILE *bmp_fp = fopen(argv[1], "rb"); + if (!bmp_fp) + err_exit(FILE_OPEN_ERR); + if (!is_bmp(bmp_fp)) + err_exit(FILE_TYPE_ERR); + + /* open jpeg file */ + FILE *jpeg_fp = fopen(argv[2], "wb"); + if (!jpeg_fp) + err_exit(FILE_OPEN_ERR); + + /* get bmp info */ + bmp_info binfo; + read_bmp(bmp_fp, &binfo); + + /* init memory for input and output */ + compress_io cio; + int in_size = (binfo.width * 3 + 3) / 4 * 4 * DCTSIZE; + int out_size = MEM_OUT_SIZE; + init_mem(&cio, bmp_fp, in_size, jpeg_fp, out_size); + + /* main encode process */ + jpeg_encode(&cio, &binfo); + + /* flush and free memory, close files */ + if (! (cio.out->flush_buffer) (&cio)) + err_exit(BUFFER_WRITE_ERR); + free_mem(&cio); + fclose(bmp_fp); + fclose(jpeg_fp); + } + else + print_help(); + exit(0); +} +#endif // main_lib diff --git a/LumaBmp2NinJpg/source/cjpeg.h b/LumaBmp2NinJpg/source/cjpeg.h new file mode 100644 index 0000000..adbd1ce --- /dev/null +++ b/LumaBmp2NinJpg/source/cjpeg.h @@ -0,0 +1,310 @@ + +#ifndef __CJPEG_H +#define __CJPEG_H + +#include +#include +#include + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; + +typedef char INT8; +typedef short INT16; +typedef int INT32; + +//typedef unsigned char bool; /* no bool type in C */ +//#define true 1; +//#define false 0; + + +#define FILE_TYPE_ERR "file type error", 1 +#define FILE_OPEN_ERR "fopen: open file error", 2 +#define FILE_READ_ERR "fread: read file error", 3 +#define BUFFER_ALLOC_ERR "malloc: alloc buffer error", 4 +#define BUFFER_READ_ERR "fread: read buffer error", 5 +#define BUFFER_WRITE_ERR "fwrite: write buffer error", 6 + + +#define REVERSED /* regularly, BMP image is stored reversely */ + +#define MEM_OUT_SIZE 1 << 17 /* alloc output memory with 128 KB */ +#define BMP_HEAD_LEN 54 /* file head length of BMP image */ +#define COMP_NUM 3 /* number of components */ +#define PRECISION 8 /* data precision */ +#define DCTSIZE 8 /* data unit size */ +#define DCTSIZE2 64 /* data unit value number */ + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +typedef struct { + UINT8 len; + UINT16 val; +} BITS; + + +/* zigzag table */ + +static UINT8 ZIGZAG[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + + +/* RGB to YCbCr table */ + +typedef struct { + INT32 r2y[256]; + INT32 r2cb[256]; + INT32 r2cr[256]; + INT32 g2y[256]; + INT32 g2cb[256]; + INT32 g2cr[256]; + INT32 b2y[256]; + INT32 b2cb[256]; + INT32 b2cr[256]; +} ycbcr_tables; + +extern ycbcr_tables ycc_tables; + +/* store color unit in YCbCr */ + +typedef struct { + float y[DCTSIZE2]; + float cb[DCTSIZE2]; + float cr[DCTSIZE2]; +} ycbcr_unit; + + +/* standard quantization tables */ + +static UINT8 STD_LU_QTABLE[DCTSIZE2] = { /* luminance */ + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static UINT8 STD_CH_QTABLE[DCTSIZE2] = { /* chrominance */ + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +static double AAN_SCALE_FACTOR[DCTSIZE] = { + 1.0, + 1.387039845, + 1.306562965, + 1.175875602, + 1.0, + 0.785694958, + 0.541196100, + 0.275899379 +}; + +/* store scaled quantization tables */ + +typedef struct { + UINT8 lu[DCTSIZE2]; + UINT8 ch[DCTSIZE2]; +} quant_tables; + +extern quant_tables q_tables; + +/* store color unit after quantizing operation */ + +typedef struct { + INT16 y[DCTSIZE2]; + INT16 cb[DCTSIZE2]; + INT16 cr[DCTSIZE2]; +} quant_unit; + + +/* standard huffman tables */ + +/* luminance DC */ + +static UINT8 STD_LU_DC_NRCODES[17] = { /* code No. */ + 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + +static UINT8 STD_LU_DC_VALUES[12] = { /* code value */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + +/* chrominance DC */ + +static UINT8 STD_CH_DC_NRCODES[17] = { + 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}; + +static UINT8 STD_CH_DC_VALUES[12] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; + +/* luminance AC */ + +static UINT8 STD_LU_AC_NRCODES[17] = { + 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d}; + +static UINT8 STD_LU_AC_VALUES[162] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/* chrominance AC */ + +static UINT8 STD_CH_AC_NRCODES[17] = { + 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77}; + +static UINT8 STD_CH_AC_VALUES[162] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +/* store precalculated huffman tables */ + +typedef struct { + BITS lu_dc[12]; + BITS lu_ac[256]; + BITS ch_dc[12]; + BITS ch_ac[256]; +} huff_tables; + +extern huff_tables h_tables; + + +/* store BMP image informations */ + +typedef struct { + UINT32 size; /* bmp file size: 2- 5 */ + UINT32 offset; /* offset between file start and data: 10-13 */ + UINT32 width; /* pixel width of bmp image: 18-21 */ + UINT32 height; /* pixel height of bmp image: 22-25 */ + UINT16 bitppx; /* bit number per pixel: 28-29 */ + UINT32 datasize; /* image data size: 34-37 */ +} bmp_info; + + +extern void err_exit(const char *error_string, int exit_num); + + +#endif /* __CJPEG_H */ + diff --git a/LumaBmp2NinJpg/source/cmarker.c b/LumaBmp2NinJpg/source/cmarker.c new file mode 100644 index 0000000..f619d72 --- /dev/null +++ b/LumaBmp2NinJpg/source/cmarker.c @@ -0,0 +1,232 @@ +/* Copyright (C) 2009 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + + + +#include "cjpeg.h" +#include "cio.h" + +/* + * Length of APP0 block (2 bytes) + * Block ID (4 bytes - ASCII "JFIF") + * Zero byte (1 byte to terminate the ID string) + * Version Major, Minor (2 bytes - major first) + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + * Xdpu (2 bytes - dots per unit horizontal) + * Ydpu (2 bytes - dots per unit vertical) + * Thumbnail X size (1 byte) + * Thumbnail Y size (1 byte) + */ +void +write_app0(compress_io *cio) +{ + write_marker(cio, M_APP0); + write_word(cio, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + write_byte(cio, 0x4A); /* Identifier: ASCII "JFIF" */ + write_byte(cio, 0x46); + write_byte(cio, 0x49); + write_byte(cio, 0x46); + write_byte(cio, 0); + write_byte(cio, 1); /* Version fields */ + write_byte(cio, 1); + write_byte(cio, 0); /* Pixel size information */ + write_word(cio, 1); + write_word(cio, 1); + write_byte(cio, 0); /* No thumbnail image */ + write_byte(cio, 0); +} + +void +write_sof0(compress_io *cio, bmp_info *binfo) +{ + write_marker(cio, M_SOF0); + write_word(cio, 3 * COMP_NUM + 2 + 5 + 1); /* length */ + write_byte(cio, PRECISION); + + write_word(cio, binfo->height); + write_word(cio, binfo->width); + + write_byte(cio, COMP_NUM); + + /* + * Component: + * Component ID + * Sampling factors: bit 0-3 vert., 4-7 hor. + * Quantization table No. + */ + /* component Y */ + write_byte(cio, 1); + write_byte(cio, 0x11); + write_byte(cio, 0); + /* component Cb */ + write_byte(cio, 2); + write_byte(cio, 0x11); + write_byte(cio, 1); + /* component Cr */ + write_byte(cio, 3); + write_byte(cio, 0x11); + write_byte(cio, 1); +} + +void +write_sos(compress_io *cio) +{ + write_marker(cio, M_SOS); + write_word(cio, 2 + 1 + COMP_NUM * 2 + 3); /* length */ + + write_byte(cio, COMP_NUM); + + /* + * Component: + * Component ID + * DC & AC table No. bits 0..3: AC table (0..3), + * bits 4..7: DC table (0..3) + */ + /* component Y */ + write_byte(cio, 1); + write_byte(cio, 0x00); + /* component Cb */ + write_byte(cio, 2); + write_byte(cio, 0x11); + /* component Cr */ + write_byte(cio, 3); + write_byte(cio, 0x11); + + write_byte(cio, 0); /* Ss */ + write_byte(cio, 0x3F); /* Se */ + write_byte(cio, 0); /* Bf */ +} + +void +write_dqt(compress_io *cio) +{ + /* index: + * bit 0..3: number of QT, Y = 0 + * bit 4..7: precision of QT, 0 = 8 bit + */ + int index; + int i; + write_marker(cio, M_DQT); + write_word(cio, 2 + (DCTSIZE2 + 1) * 2); + + index = 0; /* table for Y */ + write_byte(cio, index); + for (i = 0; i < DCTSIZE2; i++) + write_byte(cio, q_tables.lu[i]); + + index = 1; /* table for Cb,Cr */ + write_byte(cio, index); + for (i = 0; i < DCTSIZE2; i++) + write_byte(cio, q_tables.ch[i]); +} + +int +get_ht_length(UINT8 *nrcodes) +{ + int length = 0; + int i; + for (i = 1; i <= 16; i++) + length += nrcodes[i]; + return length; +} + +void +write_htable(compress_io *cio, + UINT8 *nrcodes, UINT8 *values, int len, UINT8 index) +{ + /* + * index: + * bit 0..3: number of HT (0..3), for Y = 0 + * bit 4 : type of HT, 0 = DC table, 1 = AC table + * bit 5..7: not used, must be 0 + */ + write_byte(cio, index); + + int i; + for (i = 1; i <= 16; i++) + write_byte(cio, nrcodes[i]); + for (i = 0; i < len; i++) + write_byte(cio, values[i]); +} + +void +write_dht(compress_io *cio) +{ + int len1, len2, len3, len4; + + write_marker(cio, M_DHT); + + len1 = get_ht_length(STD_LU_DC_NRCODES); + len2 = get_ht_length(STD_LU_AC_NRCODES); + len3 = get_ht_length(STD_CH_DC_NRCODES); + len4 = get_ht_length(STD_CH_AC_NRCODES); + write_word(cio, 2 + (1 + 16) * 4 + len1 + len2 + len3 + len4); + + write_htable(cio, STD_LU_DC_NRCODES, STD_LU_DC_VALUES, len1, 0x00); + write_htable(cio, STD_LU_AC_NRCODES, STD_LU_AC_VALUES, len2, 0x10); + write_htable(cio, STD_CH_DC_NRCODES, STD_CH_DC_VALUES, len3, 0x01); + write_htable(cio, STD_CH_AC_NRCODES, STD_CH_AC_VALUES, len4, 0x11); +} + +/* + * Write datastream header. + * This consists of an SOI and optional APPn markers. + */ +void +write_file_header(compress_io *cio) +{ + write_marker(cio, M_SOI); + write_app0(cio); +} + +/* + * Write frame header. + * This consists of DQT and SOFn markers. + * Note that we do not emit the SOF until we have emitted the DQT(s). + * This avoids compatibility problems with incorrect implementations that + * try to error-check the quant table numbers as soon as they see the SOF. + */ +void +write_frame_header(compress_io *cio, bmp_info *binfo) +{ + write_dqt(cio); + write_sof0(cio, binfo); +} + +/* + * Write scan header. + * This consists of DHT or DAC markers, optional DRI, and SOS. + * Compressed data will be written following the SOS. + */ +void +write_scan_header(compress_io *cio) +{ + write_dht(cio); + write_sos(cio); +} + +/* + * Write datastream trailer. + */ +void +write_file_trailer(compress_io *cio) +{ + write_marker(cio, M_EOI); +} + diff --git a/LumaBmp2NinJpg/source/fdctflt.c b/LumaBmp2NinJpg/source/fdctflt.c new file mode 100644 index 0000000..d9ac71d --- /dev/null +++ b/LumaBmp2NinJpg/source/fdctflt.c @@ -0,0 +1,124 @@ + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#include "cjpeg.h" + +/* + * Perform the forward DCT on one block of samples. + */ + +void +jpeg_fdct(float *data) +{ + float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + float tmp10, tmp11, tmp12, tmp13; + float z1, z2, z3, z4, z5, z11, z13; + float *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + /* Load data into workspace */ + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Apply unsigned->signed conversion */ + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ + z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((float) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ + z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((float) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + diff --git a/LumaBmp2NinJpg/source/main.c b/LumaBmp2NinJpg/source/main.c new file mode 100644 index 0000000..b59b84a --- /dev/null +++ b/LumaBmp2NinJpg/source/main.c @@ -0,0 +1,516 @@ +#include +#include +#include +#include +#include +#include +//#include "cjpeg.h" +//#include "cio.h" + +#ifdef __3DS__ + #include <3ds.h> +#endif + +#define PathStringSize 128 + +#ifdef __3DS__ + #define ScreenshotsPath "sdmc:/luma/screenshots/" + #define ScreenshotsBakPath "sdmc:/luma/screenshots-old/" + #define DcimPath "sdmc:/DCIM/" + #define mkdir(path) { mkdir(path, 7777); } + #define DirListPrepare(dir) {} +#else + #define ScreenshotsPath "./luma/screenshots/" + #define ScreenshotsBakPath "./luma/screenshots-old/" + #define DcimPath "./DCIM/" + #define DirListPrepare(dir) { readdir(dir); readdir(dir); } // exclude `.` and `..` from list +#endif + +#define IsStringEqual(s1, s2) ( strcmp(s1, s2) == 0 ) +#define IsStringEmpty(str) ( IsStringEqual(str, "") ) +#define ExistDirectory(path) ( opendir(path) != NULL ) +#define MkDirIfNotExist(path) { if (!ExistDirectory(path)) mkdir(path); } +#define Try(flag, then) { if (!(flag)) { then; printf("A fatal error occured.\n"); Exit(); } } + +void Exit (void) +{ + printf("The program will now exit.\n"); + #ifdef __3DS__ + sleep(3); + gfxExit(); + #endif + exit(0); +} + +char *GetLastDirectoryItem (const char path[], char *buffer) +{ + DIR *directory; + struct dirent *dirEntry; + memset(buffer, 0, PathStringSize); + if ((directory = opendir(path)) != NULL) { + DirListPrepare(directory); + while ((dirEntry = readdir(directory)) != NULL) { + strcpy(buffer, dirEntry->d_name); + } + closedir(directory); + } else { + printf("Error reading `%s` directory.\n", path); + Exit(); + } + return buffer; +} + +bool IsDirectoryEmpty (char *path) { + char buffer[PathStringSize]; + return IsStringEmpty(GetLastDirectoryItem(path, buffer)); +} + +char *GetDcimNinNameFromIndex (int index, char *buffer) +{ + char indexString[5]; + sprintf(buffer, "%dNIN03/", index); + return buffer; +} + +char *GetDcimNinFullPathFromIndex (int index, char *buffer) +{ + char name[PathStringSize]; + return strcat( strcpy(buffer, DcimPath), GetDcimNinNameFromIndex(index, name) ); +} + +void *MakeDcimNinFolderFromIndex (int index) +{ + char path[PathStringSize]; + mkdir(GetDcimNinFullPathFromIndex(index, path)); +} + +int GetDcimNinIndexFromPath (char *path) +{ + char indexStr[4]; + indexStr[0] = path[0]; + indexStr[1] = path[1]; + indexStr[2] = path[2]; + return atoi(indexStr); +} + +int GetHniXIndexFromPath (char *path) +{ + char indexStr[5]; + int pathExtIndex = (strlen(path) - 1 - 4); + indexStr[0] = path[pathExtIndex - 3]; + indexStr[1] = path[pathExtIndex - 2]; + indexStr[2] = path[pathExtIndex - 1]; + indexStr[3] = path[pathExtIndex - 0]; + //printf(". %s %s\n", path, indexStr); + return atoi(indexStr); +} + +bool IsXNinFolderFullFromIndex (int index) +{ + char path[PathStringSize], buffer[PathStringSize]; + char *curXNinFullPath = GetDcimNinFullPathFromIndex(index, path); + char *lastHniXPath = GetLastDirectoryItem(curXNinFullPath, buffer); + int lastHniXIndex = GetHniXIndexFromPath(lastHniXPath); + //printf("%s %s %d", curXNinFullPath, lastHniXPath, lastHniXIndex); + return ( lastHniXIndex >= 100 ); +} + +//bool ConvertImage (char *bmpPath, char *jpgPath) { +/* FILE *bmpFile, *jpgFile; + bmp_info bmpInfo; + compress_io compressIo; + + if ( (bmpFile = fopen(bmpPath, "rb")) == NULL || !is_bmp(bmpFile) ) { + return false; + } + + if ( (jpgFile = fopen(jpgPath, "wb")) == NULL ) { + return false; + } + + read_bmp(bmpFile, &bmpInfo); + init_mem(&compressIo, bmpFile, ((bmpInfo.width * 3 + 3) / 4 * 4 * DCTSIZE), jpgFile, MEM_OUT_SIZE); + jpeg_encode(&compressIo, &bmpInfo); + + if ( !(compressIo.out->flush_buffer)(&compressIo) ) { + return false; + } + + free_mem(&compressIo); + fclose(bmpFile); + fclose(jpgFile); + return true; +*/ +//} + +char *ReadFilepathIntoBuffer (char filepath[], size_t *sizeOut) { +//size_t ReadFilepathIntoBuffer (char filepath[], char *buffer) { + size_t size; + char *buffer; + FILE *file; + Try((file = fopen(filepath, "rb")), {}); + fseek(file, 0L, SEEK_END); + size = ftell(file); + rewind(file); + Try((buffer = calloc(1, size+1)), { fclose(file); }) + if ( fread(buffer, size, 1, file) != 1 ) { + fclose(file); + free(buffer); + Exit(); + } + fclose(file); + *sizeOut = size; + return buffer;//size; +} + +void WriteBufferIntoFilepath(char *buffer, size_t size, char filepath[]) { + FILE *file; + Try((file= fopen(filepath, "wb")), {}); + fwrite(buffer, size, 1, file); + fclose(file); +} + +#ifdef __3DS__ +u8 *HttpPostBmpForJpg(const char* url, const char* data, size_t len, /*char *outBuffer*/size_t *outSize) { + Result ret=0; + httpcContext context; + u32 statuscode=0; + u32 contentsize=0, readsize=0, size=0; + u8 *buf, *lastbuf; + + httpcOpenContext(&context, HTTPC_METHOD_POST, url, 0); + httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); + httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED); + httpcAddRequestHeaderField(&context, "User-Agent", "LumaBmp2NinJpg/1.0.0"); + httpcAddRequestHeaderField(&context, "Content-Type", "image/bmp"); + httpcAddPostDataRaw(&context, (u32*)data, len); + + //printf("1\n"); + //sleep(1); + + ret = httpcBeginRequest(&context); + if(ret!=0){ + httpcCloseContext(&context); + return ret; + } + + //printf("2\n"); + //sleep(1); + + ret = httpcGetResponseStatusCode(&context, &statuscode); + //printf("r %d %d\n",ret,statuscode); + //sleep(1); + if(ret!=0){ + httpcCloseContext(&context); + return ret; + } + + //printf("3\n"); + //sleep(1); + + if(statuscode!=200){ + httpcCloseContext(&context); + return -2; + } + + //printf("4\n"); + //sleep(1); + + ret=httpcGetDownloadSizeState(&context, NULL, &contentsize); + if(ret!=0){ + httpcCloseContext(&context); + return ret; + } + + //printf("5\n"); + //sleep(1); + + buf = (u8*)malloc(0x1000); + if(buf==NULL){ + httpcCloseContext(&context); + return -1; + } + + //printf("6\n"); + //sleep(1); + + do { + // This download loop resizes the buffer as data is read. + ret = httpcDownloadData(&context, buf+size, 0x1000, &readsize); + size += readsize; + if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING){ + lastbuf = buf; // Save the old pointer, in case realloc() fails. + buf = realloc(buf, size + 0x1000); + if(buf==NULL){ + httpcCloseContext(&context); + free(lastbuf); + return -1; + } + } + } while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING); + + //printf("7\n"); + //sleep(1); + + if(ret!=0){ + httpcCloseContext(&context); + free(buf); + return -1; + } + + //printf("8\n"); + //sleep(1); + + // Resize the buffer back down to our actual final size + lastbuf = buf; + buf = realloc(buf, size); + if(buf==NULL){ // realloc() failed. + httpcCloseContext(&context); + free(lastbuf); + return -1; + } + + //printf("9\n"); + //sleep(1); + + httpcCloseContext(&context); + *outSize = size; + return buf;//size; +/* + Result ret=0; + httpcContext context; + char *newurl=NULL; + u32 statuscode=0; + u32 contentsize=0, readsize=0, size=0; + u8 *buf, *lastbuf; + + printf("POSTing %s\n", url); + + do { + ret = httpcOpenContext(&context, HTTPC_METHOD_POST, url, 0); + printf("return from httpcOpenContext: %" PRIx32 "\n",ret); + + // This disables SSL cert verification, so https:// will be usable + ret = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify); + printf("return from httpcSetSSLOpt: %" PRIx32 "\n",ret); + + // Enable Keep-Alive connections + ret = httpcSetKeepAlive(&context, HTTPC_KEEPALIVE_ENABLED); + printf("return from httpcSetKeepAlive: %" PRIx32 "\n",ret); + + // Set a User-Agent header so websites can identify your application + ret = httpcAddRequestHeaderField(&context, "User-Agent", "LumaBmp2NinJpg/1.0.0"); + printf("return from httpcAddRequestHeaderField: %" PRIx32 "\n",ret); + + // Set a Content-Type header so websites can identify the format of our raw body data. + // If you want to send form data in your request, use: + //ret = httpcAddRequestHeaderField(&context, "Content-Type", "multipart/form-data"); + // If you want to send raw JSON data in your request, use: + ret = httpcAddRequestHeaderField(&context, "Content-Type", "image/bmp");//"application/json"); + printf("return from httpcAddRequestHeaderField: %" PRIx32 "\n",ret); + + // Post specified data. + // If you want to add a form field to your request, use: + //ret = httpcAddPostDataAscii(&context, "data", value); + // If you want to add a form field containing binary data to your request, use: + //ret = httpcAddPostDataBinary(&context, "field name", yourBinaryData, length); + // If you want to add raw data to your request, use: + ret = httpcAddPostDataRaw(&context, (u32*)data, len); + printf("return from httpcAddPostDataRaw: %" PRIx32 "\n",ret); + + ret = httpcBeginRequest(&context); + if(ret!=0){ + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + return ret; + } + + ret = httpcGetResponseStatusCode(&context, &statuscode); + if(ret!=0){ + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + return ret; + } + + if ((statuscode >= 301 && statuscode <= 303) || (statuscode >= 307 && statuscode <= 308)) { + if(newurl==NULL) newurl = malloc(0x1000); // One 4K page for new URL + if (newurl==NULL){ + httpcCloseContext(&context); + return -1; + } + ret = httpcGetResponseHeader(&context, "Location", newurl, 0x1000); + url = newurl; // Change pointer to the url that we just learned + printf("redirecting to url: %s\n",url); + httpcCloseContext(&context); // Close this context before we try the next + } + } while ((statuscode >= 301 && statuscode <= 303) || (statuscode >= 307 && statuscode <= 308)); + + if(statuscode!=200){ + printf("URL returned status: %" PRIx32 "\n", statuscode); + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + return -2; + } + + // This relies on an optional Content-Length header and may be 0 + ret=httpcGetDownloadSizeState(&context, NULL, &contentsize); + if(ret!=0){ + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + return ret; + } + + printf("reported size: %" PRIx32 "\n",contentsize); + + // Start with a single page buffer + buf = (u8*)malloc(0x1000); + if(buf==NULL){ + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + return -1; + } + + do { + // This download loop resizes the buffer as data is read. + ret = httpcDownloadData(&context, buf+size, 0x1000, &readsize); + size += readsize; + if (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING){ + lastbuf = buf; // Save the old pointer, in case realloc() fails. + buf = realloc(buf, size + 0x1000); + if(buf==NULL){ + httpcCloseContext(&context); + free(lastbuf); + if(newurl!=NULL) free(newurl); + return -1; + } + } + } while (ret == (s32)HTTPC_RESULTCODE_DOWNLOADPENDING); + + if(ret!=0){ + httpcCloseContext(&context); + if(newurl!=NULL) free(newurl); + free(buf); + return -1; + } + + // Resize the buffer back down to our actual final size + lastbuf = buf; + buf = realloc(buf, size); + if(buf==NULL){ // realloc() failed. + httpcCloseContext(&context); + free(lastbuf); + if(newurl!=NULL) free(newurl); + return -1; + } + + printf("response size: %" PRIx32 "\n",size); + + printf("%s\n",buf); + + gfxFlushBuffers(); + gfxSwapBuffers(); + + httpcCloseContext(&context); + //free(buf); + if (newurl!=NULL) free(newurl); + + *outSize = size; + return buf;//size; + */ +} +#endif + +int main (int argc, char** argv) +{ + int dcimFolderIndex = 100; + char initDcimNinPath[PathStringSize]; + + #ifdef __3DS__ + PrintConsole bottomScreen; + gfxInitDefault(); + consoleInit(GFX_BOTTOM, &bottomScreen); + consoleSelect(&bottomScreen); + httpcInit(4 * 1024 * 1024); + #endif + +//size_t len1; +//char *buf1 = HttpPostBmpForJpg("https://httpbin.org/post", "{\"foo\": \"bar\"}", 19, &len1); +//printf("%d %s\n",len1,buf1); +//sleep(1); + + if (!ExistDirectory(ScreenshotsPath)) { + printf("The `%s` directory doesn't exist. Nothing to do.\n", ScreenshotsPath); + Exit(); + } + + if (IsDirectoryEmpty(ScreenshotsPath)) { + printf("No files present in `%s`. Nothing to do.\n", ScreenshotsPath); + Exit(); + } + + MkDirIfNotExist(ScreenshotsBakPath); + MkDirIfNotExist(DcimPath); + + if (IsDirectoryEmpty(DcimPath)) { + MakeDcimNinFolderFromIndex(dcimFolderIndex); + } + + GetLastDirectoryItem(DcimPath, initDcimNinPath); + dcimFolderIndex = GetDcimNinIndexFromPath(initDcimNinPath); + + struct dirent *dirEntry; + DIR *directory = opendir(ScreenshotsPath); + DirListPrepare(directory); + while ((dirEntry = readdir(directory)) != NULL) { + char srcPath[PathStringSize], srcPathBak[PathStringSize], dstName[PathStringSize], dstPath[PathStringSize]; + char currDcimNinName[PathStringSize], currDcimNinPath[PathStringSize], lastJpgName[PathStringSize]; + + if (IsXNinFolderFullFromIndex(dcimFolderIndex)) { + MakeDcimNinFolderFromIndex(++dcimFolderIndex); + } + + GetLastDirectoryItem(DcimPath, currDcimNinName); + sprintf(currDcimNinPath, "%s%s/", DcimPath, currDcimNinName); + + GetLastDirectoryItem(currDcimNinPath, lastJpgName); + int currJpgIndex = (GetHniXIndexFromPath(lastJpgName) + 1); + + sprintf(srcPath, "%s%s", ScreenshotsPath, dirEntry->d_name); + sprintf(srcPathBak, "%s%s", ScreenshotsBakPath, dirEntry->d_name); + sprintf(dstName, "%dNIN03/HNI_%04d.JPG", dcimFolderIndex, currJpgIndex); + sprintf(dstPath, "%s%s", DcimPath, dstName); + printf("* %s => %s ...", dirEntry->d_name, dstName); + + #ifdef __3DS__ + char *bmpData, *jpgData; + size_t bmpSize, jpgSize; + //size_t bmpSize = ReadFilepathIntoBuffer(srcPath, bmpData); + bmpData = ReadFilepathIntoBuffer(srcPath, &bmpSize); + //printf("%d %s\n", bmpSize, bmpData); + //sleep(3); + //size_t jpgSize = HttpPostBmpForJpg("https://hlb0.octt.eu.org/LumaBmp2NinJpgConvert.php", bmpData, bmpSize, jpgData); + jpgData = HttpPostBmpForJpg("http://hlb0.octt.eu.org/LumaBmp2NinJpgConvert.php", bmpData, bmpSize, &jpgSize); + //sleep(5); + //printf("%d %s\n", jpgSize, jpgData); + //sleep(3); + free(bmpData); + if (jpgSize > 0) { + printf("Done!\n"); + WriteBufferIntoFilepath(jpgData, jpgSize, dstPath); + #endif + rename(srcPath, srcPathBak); + //rename(srcPath, dstPath); + #ifndef __3DS__ + printf("\n"); + #endif + #ifdef __3DS__ + } else { + printf("Fail!\n"); + } + free(jpgData); + #endif + } + closedir(directory); + + Exit(); +} diff --git a/LumaBmp2NinJpg/source/rdbmp.c b/LumaBmp2NinJpg/source/rdbmp.c new file mode 100644 index 0000000..9214301 --- /dev/null +++ b/LumaBmp2NinJpg/source/rdbmp.c @@ -0,0 +1,50 @@ + +#include "cjpeg.h" + +UINT32 +extract_uint(const UINT8 *dataptr, UINT32 start, UINT32 len) +{ + UINT32 uint = 0; + if (len <= 0 || len > 4) + return uint; + if (len > 0) + uint += dataptr[start]; + if (len > 1) + uint += dataptr[start+1] * 1 << 8; + if (len > 2) + uint += dataptr[start+2] * 1 << 16; + if (len > 3) + uint += dataptr[start+3] * 1 << 24; + return uint; +} + +int +get_file_size(FILE *bmp_fp) +{ + fpos_t fpos; + int len; + fgetpos(bmp_fp, &fpos); + fseek(bmp_fp, 0, SEEK_END); + len = ftell(bmp_fp); + fsetpos(bmp_fp, &fpos); + return len; +} + +void +read_bmp(FILE *bmp_fp, bmp_info *binfo) +{ + size_t len = BMP_HEAD_LEN; + UINT8 bmp_head[len]; + if (fread(bmp_head, sizeof (UINT8), len, bmp_fp) != len) + err_exit(FILE_READ_ERR); + + binfo->size = extract_uint(bmp_head, 2, 4); + binfo->offset = extract_uint(bmp_head, 10, 4); + binfo->width = extract_uint(bmp_head, 18, 4); + binfo->height = extract_uint(bmp_head, 22, 4); + binfo->bitppx = extract_uint(bmp_head, 28, 2); + binfo->datasize = extract_uint(bmp_head, 34, 4); + if (binfo->datasize == 0) /* data size not included in some BMP */ + binfo->datasize = get_file_size(bmp_fp) - binfo->offset; +} +