mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
Merge pull request #77 from kwagyeman/master
Add BMP and P2/3/5/6 file format support.
This commit is contained in:
commit
1e4678ccd7
@ -138,6 +138,7 @@ OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/img/,\
|
||||
rgb2rgb_tab.o \
|
||||
median.o \
|
||||
point.o \
|
||||
bmp.o \
|
||||
ppm.o \
|
||||
rectangle.o \
|
||||
fast.o \
|
||||
|
||||
@ -41,6 +41,7 @@ SRCS += $(addprefix img/, \
|
||||
rgb2rgb_tab.c \
|
||||
median.c \
|
||||
point.c \
|
||||
bmp.c \
|
||||
ppm.c \
|
||||
rectangle.c \
|
||||
fast.c \
|
||||
|
||||
317
src/omv/img/bmp.c
Normal file
317
src/omv/img/bmp.c
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* This file is part of the OpenMV project.
|
||||
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
|
||||
* This work is licensed under the MIT license, see the file LICENSE for details.
|
||||
*
|
||||
* BMP reader/writer.
|
||||
*
|
||||
*/
|
||||
#include <arm_math.h>
|
||||
#include <stdlib.h>
|
||||
#include <ff.h>
|
||||
#include "ff_wrapper.h"
|
||||
#include "xalloc.h"
|
||||
#include "imlib.h"
|
||||
|
||||
// Store mode settings internally, no need for callers to store this info.
|
||||
static int32_t bmp_w;
|
||||
static int32_t bmp_h;
|
||||
static uint16_t bmp_bpp;
|
||||
static uint32_t bmp_fmt;
|
||||
static uint32_t bmp_row_bytes;
|
||||
|
||||
// This function inits the geometry values of an image.
|
||||
int bmp_read_geometry(FIL *fp, image_t *img, const char *path)
|
||||
{
|
||||
FRESULT res = f_open(fp, path, FA_READ|FA_OPEN_EXISTING);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
READ_BYTE_EXPECT(fp, 'B');
|
||||
READ_BYTE_EXPECT(fp, 'M');
|
||||
uint32_t file_size;
|
||||
READ_LONG(fp, &file_size);
|
||||
READ_LONG_IGNORE(fp);
|
||||
READ_LONG_IGNORE(fp);
|
||||
|
||||
uint32_t header_size;
|
||||
READ_LONG(fp, &header_size);
|
||||
if (file_size <= header_size) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t data_size = file_size - header_size;
|
||||
if (data_size % 4) { // must be some number of dwords
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
READ_LONG_EXPECT(fp, 40);
|
||||
READ_LONG(fp, (uint32_t*) &bmp_w);
|
||||
READ_LONG(fp, (uint32_t*) &bmp_h);
|
||||
if ((bmp_w == 0) || (bmp_h == 0)) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
img->w = abs(bmp_w);
|
||||
img->h = abs(bmp_h);
|
||||
|
||||
READ_WORD_EXPECT(fp, 1);
|
||||
READ_WORD(fp, &bmp_bpp);
|
||||
if ((bmp_bpp != 8) && (bmp_bpp != 16) && (bmp_bpp != 24)) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
img->bpp = (bmp_bpp == 8) ? 1 : 2;
|
||||
|
||||
READ_LONG(fp, &bmp_fmt);
|
||||
if ((bmp_fmt != 0) && (bmp_fmt != 3)) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
READ_LONG_EXPECT(fp, data_size);
|
||||
READ_LONG_IGNORE(fp);
|
||||
READ_LONG_IGNORE(fp);
|
||||
READ_LONG_IGNORE(fp);
|
||||
READ_LONG_IGNORE(fp);
|
||||
|
||||
if (bmp_bpp == 8) {
|
||||
if (bmp_fmt != 0) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
// Color Table (1024 bytes)
|
||||
for (int i = 0; i < 256; i++) {
|
||||
READ_LONG_EXPECT(fp, ((i) << 16) | ((i) << 8) | i);
|
||||
}
|
||||
} else if (bmp_bpp == 16) {
|
||||
if (bmp_fmt != 3) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
// Bit Masks (12 bytes)
|
||||
READ_LONG_EXPECT(fp, 0x1F << 11);
|
||||
READ_LONG_EXPECT(fp, 0x3F << 5);
|
||||
READ_LONG_EXPECT(fp, 0x1F);
|
||||
} else if (bmp_bpp == 24) {
|
||||
if (bmp_fmt == 3) {
|
||||
// Bit Masks (12 bytes)
|
||||
READ_LONG_EXPECT(fp, 0xFF << 16);
|
||||
READ_LONG_EXPECT(fp, 0xFF << 8);
|
||||
READ_LONG_EXPECT(fp, 0xFF);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bmp_row_bytes = (((img->w * bmp_bpp) + 31) / 32) * 4;
|
||||
if (data_size != (bmp_row_bytes * img->h)) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
// This function reads the pixel values of an image.
|
||||
int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end)
|
||||
{
|
||||
if (bmp_bpp == 8) {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0; j < bmp_row_bytes; j++) {
|
||||
uint8_t pixel;
|
||||
READ_BYTE(fp, &pixel);
|
||||
if (j < img->w) {
|
||||
if (bmp_h < 0) { // vertical flip
|
||||
if (bmp_w < 0) { // horizontal flip
|
||||
IM_SET_GS_PIXEL(img, (img->w-j-1), i, pixel);
|
||||
} else {
|
||||
IM_SET_GS_PIXEL(img, j, i, pixel);
|
||||
}
|
||||
} else {
|
||||
if (bmp_w < 0) {
|
||||
IM_SET_GS_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel);
|
||||
} else {
|
||||
IM_SET_GS_PIXEL(img, j, (img->h-i-1), pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bmp_bpp == 16) {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0, jj = bmp_row_bytes / 2; j < jj; j++) {
|
||||
uint16_t pixel;
|
||||
READ_WORD(fp, &pixel);
|
||||
IM_SWAP16(pixel);
|
||||
if (j < img->w) {
|
||||
if (bmp_h < 0) { // vertical flip
|
||||
if (bmp_w < 0) { // horizontal flip
|
||||
IM_SET_RGB565_PIXEL(img, (img->w-j-1), i, pixel);
|
||||
} else {
|
||||
IM_SET_RGB565_PIXEL(img, j, i, pixel);
|
||||
}
|
||||
} else {
|
||||
if (bmp_w < 0) {
|
||||
IM_SET_RGB565_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel);
|
||||
} else {
|
||||
IM_SET_RGB565_PIXEL(img, j, (img->h-i-1), pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bmp_bpp == 24) {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0, jj = bmp_row_bytes / 3; j < jj; j++) {
|
||||
uint8_t r, g, b;
|
||||
READ_BYTE(fp, &r);
|
||||
READ_BYTE(fp, &g);
|
||||
READ_BYTE(fp, &b);
|
||||
uint16_t pixel = IM_RGB565(IM_R825(r), IM_G826(g), IM_B825(b));
|
||||
if (j < img->w) {
|
||||
if (bmp_h < 0) { // vertical flip
|
||||
if (bmp_w < 0) { // horizontal flip
|
||||
IM_SET_RGB565_PIXEL(img, (img->w-j-1), i, pixel);
|
||||
} else {
|
||||
IM_SET_RGB565_PIXEL(img, j, i, pixel);
|
||||
}
|
||||
} else {
|
||||
if (bmp_w < 0) {
|
||||
IM_SET_RGB565_PIXEL(img, (img->w-j-1), (img->h-i-1), pixel);
|
||||
} else {
|
||||
IM_SET_RGB565_PIXEL(img, j, (img->h-i-1), pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0, jj = bmp_row_bytes % 3; j < jj; j++) {
|
||||
READ_BYTE_IGNORE(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
static int bmp_read_int(image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
|
||||
int res = bmp_read_geometry(&fp, img, path);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!img->pixels) { // don't allocate if already allocated...
|
||||
img->pixels = xalloc(img->w * img->h * img->bpp);
|
||||
}
|
||||
|
||||
res = bmp_read_pixels(&fp, img, 0, img->h);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return f_close(&fp);
|
||||
}
|
||||
|
||||
int bmp_read(image_t *img, const char *path)
|
||||
{
|
||||
const uint8_t *backup = img->pixels;
|
||||
const int res = bmp_read_int(img, path);
|
||||
// free image if I didn't start with one...
|
||||
if ((res != FR_OK) && (!backup) && (img->pixels)) {
|
||||
xfree(img->pixels);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int bmp_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
{
|
||||
rectangle_t rect;
|
||||
if (!rectangle_subimg(img, r, &rect)) {
|
||||
return -1; // no image intersection
|
||||
}
|
||||
|
||||
FIL fp;
|
||||
FRESULT res = f_open(&fp, path, FA_WRITE|FA_CREATE_ALWAYS);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (IM_IS_GS(img)) {
|
||||
const int row_bytes = (((rect.w * 8) + 31) / 32) * 4;
|
||||
const int data_size = (row_bytes * rect.h);
|
||||
const int waste = (row_bytes / sizeof(uint8_t)) - rect.w;
|
||||
// File Header (14 bytes)
|
||||
WRITE_BYTE(&fp, 'B');
|
||||
WRITE_BYTE(&fp, 'M');
|
||||
WRITE_LONG(&fp, 14 + 40 + 1024 + data_size);
|
||||
WRITE_WORD(&fp, 0);
|
||||
WRITE_WORD(&fp, 0);
|
||||
WRITE_LONG(&fp, 14 + 40 + 1024);
|
||||
// Info Header (40 bytes)
|
||||
WRITE_LONG(&fp, 40);
|
||||
WRITE_LONG(&fp, rect.w);
|
||||
WRITE_LONG(&fp, -rect.h); // store the image flipped (correctly)
|
||||
WRITE_WORD(&fp, 1);
|
||||
WRITE_WORD(&fp, 8);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, data_size);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
// Color Table (1024 bytes)
|
||||
for (int i = 0; i < 256; i++) {
|
||||
WRITE_LONG(&fp, ((i) << 16) | ((i) << 8) | i);
|
||||
}
|
||||
for (int i = 0; i < rect.h; i++) {
|
||||
for (int j = 0; j < rect.w; j++) {
|
||||
WRITE_BYTE(&fp, IM_GET_GS_PIXEL(img, (rect.x + j), (rect.y + i)));
|
||||
}
|
||||
for (int j = 0; j < waste; j++) {
|
||||
WRITE_BYTE(&fp, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int row_bytes = (((rect.w * 16) + 31) / 32) * 4;
|
||||
const int data_size = (row_bytes * rect.h);
|
||||
const int waste = (row_bytes / sizeof(uint16_t)) - rect.w;
|
||||
// File Header (14 bytes)
|
||||
WRITE_BYTE(&fp, 'B');
|
||||
WRITE_BYTE(&fp, 'M');
|
||||
WRITE_LONG(&fp, 14 + 40 + 12 + data_size);
|
||||
WRITE_WORD(&fp, 0);
|
||||
WRITE_WORD(&fp, 0);
|
||||
WRITE_LONG(&fp, 14 + 40 + 12);
|
||||
// Info Header (40 bytes)
|
||||
WRITE_LONG(&fp, 40);
|
||||
WRITE_LONG(&fp, rect.w);
|
||||
WRITE_LONG(&fp, -rect.h); // store the image flipped (correctly)
|
||||
WRITE_WORD(&fp, 1);
|
||||
WRITE_WORD(&fp, 16);
|
||||
WRITE_LONG(&fp, 3);
|
||||
WRITE_LONG(&fp, data_size);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
WRITE_LONG(&fp, 0);
|
||||
// Bit Masks (12 bytes)
|
||||
WRITE_LONG(&fp, 0x1F << 11);
|
||||
WRITE_LONG(&fp, 0x3F << 5);
|
||||
WRITE_LONG(&fp, 0x1F);
|
||||
for (int i = 0; i < rect.h; i++) {
|
||||
for (int j = 0; j < rect.w; j++) {
|
||||
WRITE_WORD(&fp, IM_SWAP16(IM_GET_RGB565_PIXEL(img, (rect.x + j), (rect.y + i))));
|
||||
}
|
||||
for (int j = 0; j < waste; j++) {
|
||||
WRITE_WORD(&fp, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f_close(&fp);
|
||||
}
|
||||
@ -46,6 +46,26 @@ void imlib_set_pixel(image_t *img, int x, int y, int p)
|
||||
}
|
||||
}
|
||||
|
||||
/* those just call ppm for now */
|
||||
int imlib_load_image(image_t *image, const char *path)
|
||||
{
|
||||
return ppm_read(image, path);
|
||||
}
|
||||
|
||||
int imlib_save_image(image_t *image, const char *path, rectangle_t *r)
|
||||
{
|
||||
if (r == NULL) {
|
||||
rectangle_t rectangle;
|
||||
rectangle.x = 0;
|
||||
rectangle.y = 0;
|
||||
rectangle.w = image->w;
|
||||
rectangle.h = image->h;
|
||||
return ppm_write_subimg(image, path, &rectangle);
|
||||
} else {
|
||||
return ppm_write_subimg(image, path, r);
|
||||
}
|
||||
}
|
||||
|
||||
void imlib_draw_line(image_t *img, int x0, int y0, int x1, int y1, int c)
|
||||
{
|
||||
int dx = abs(x1-x0);
|
||||
@ -1015,21 +1035,6 @@ void imlib_histeq(struct image *src)
|
||||
}
|
||||
}
|
||||
|
||||
/* those just call ppm for now */
|
||||
int imlib_load_image(image_t *image, const char *path)
|
||||
{
|
||||
return ppm_read(image, path);
|
||||
}
|
||||
|
||||
int imlib_save_image(image_t *image, const char *path, rectangle_t *r)
|
||||
{
|
||||
if (r == NULL) {
|
||||
return ppm_write(image, path);
|
||||
} else {
|
||||
return ppm_write_subimg(image, path, r);
|
||||
}
|
||||
}
|
||||
|
||||
// One pass standard deviation
|
||||
int imlib_std(image_t *image)
|
||||
{
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#define __IMLIB_H__
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <ff.h>
|
||||
#include "array.h"
|
||||
#include "fmath.h"
|
||||
|
||||
@ -288,6 +289,18 @@ point_t *point_alloc(int16_t x, int16_t y);
|
||||
bool point_equal(point_t *p1, point_t *p2);
|
||||
float point_distance(point_t *p1, point_t *p2);
|
||||
|
||||
/* Image file functions */
|
||||
int ppm_read_geometry(FIL *fp, image_t *img, const char *path);
|
||||
int ppm_read_pixels(FIL *fp, image_t *img, int line_start, int line_end);
|
||||
int ppm_read(image_t *img, const char *path);
|
||||
int ppm_write_subimg(image_t *img, const char *path, rectangle_t *r);
|
||||
int bmp_read_geometry(FIL *fp, image_t *img, const char *path);
|
||||
int bmp_read_pixels(FIL *fp, image_t *img, int line_start, int line_end);
|
||||
int bmp_read(image_t *img, const char *path);
|
||||
int bmp_write_subimg(image_t *img, const char *path, rectangle_t *r);
|
||||
int imlib_load_image(image_t *image, const char *path);
|
||||
int imlib_save_image(image_t *image, const char *path, rectangle_t *r);
|
||||
|
||||
/* Rectangle functions */
|
||||
rectangle_t *rectangle_alloc(int16_t x, int16_t y, int16_t w, int16_t h);
|
||||
bool rectangle_equal(rectangle_t *r1, rectangle_t *r2);
|
||||
@ -392,14 +405,6 @@ void imlib_blend(struct image *src, struct image *dst, int x_off, int y_off, uin
|
||||
void imlib_subimage(struct image *src, struct image *dst, int x_off, int y_off);
|
||||
void jpeg_compress(image_t *src, image_t *dst, int quality);
|
||||
|
||||
/* Image file functions */
|
||||
int ppm_read(image_t *img, const char *path);
|
||||
int ppm_write(image_t *img, const char *path);
|
||||
int ppm_write_subimg(image_t *img, const char *path, rectangle_t *r);
|
||||
int imlib_load_image(image_t *image, const char *path);
|
||||
int imlib_save_image(image_t *image, const char *path, rectangle_t *r);
|
||||
|
||||
|
||||
// Image filter functions
|
||||
void im_filter_bw(uint8_t *src, uint8_t *dst, int size, int bpp, void *args);
|
||||
void im_filter_skin(uint8_t *src, uint8_t *dst, int size, int bpp, void *args);
|
||||
|
||||
@ -6,253 +6,214 @@
|
||||
* PPM/PGM reader/writer.
|
||||
*
|
||||
*/
|
||||
#include <ff.h>
|
||||
#include <stdio.h>
|
||||
#include <ff.h>
|
||||
#include "ff_wrapper.h"
|
||||
#include "xalloc.h"
|
||||
#include "imlib.h"
|
||||
#include "mdefs.h"
|
||||
|
||||
#define BUF_LEN (1024*3)
|
||||
|
||||
/*
|
||||
* The following macros and lookup tables extract and convert
|
||||
* packed RGB565 to RGB888, this saves 1 mult and 1 div later.
|
||||
* Note the packed RGB565 pixel is accessed LSB first in memory.
|
||||
*/
|
||||
#define R8(p) \
|
||||
rb_tbl[((p>>3)&0x1F)]
|
||||
|
||||
#define G8(p) \
|
||||
g_tbl[((p&0x07)<<3)|(p>>13)]
|
||||
|
||||
#define B8(p) \
|
||||
rb_tbl[((p>>8)&0x1F)]
|
||||
|
||||
static uint8_t rb_tbl []= {
|
||||
0, 8, 16, 24, 32, 41, 49, 57, 65, 74,
|
||||
82, 90, 98, 106, 115, 123, 131, 139,
|
||||
148, 156, 164, 172, 180, 189, 197, 205,
|
||||
213, 222, 230, 238, 246, 255
|
||||
};
|
||||
|
||||
static uint8_t g_tbl []= {
|
||||
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
|
||||
48, 52, 56, 60, 64, 68, 72, 76, 80, 85, 89,
|
||||
93, 97, 101, 105, 109, 113, 117, 121, 125, 129,
|
||||
133, 137, 141, 145, 149, 153, 157, 161, 165, 170,
|
||||
174, 178, 182, 186, 190, 194, 198, 202, 206, 210,
|
||||
214, 218, 222, 226, 230, 234, 238, 242, 246, 250, 255
|
||||
};
|
||||
|
||||
static int isspace(int c)
|
||||
{
|
||||
return (c=='\n'||c=='\r'||c=='\t'||c==' ');
|
||||
static uint8_t read_int_c;
|
||||
static bool read_int_c_valid;
|
||||
static void read_int_reset() {
|
||||
read_int_c_valid = false;
|
||||
}
|
||||
|
||||
static char f_getc(FIL *fp)
|
||||
static int read_int(FIL *fp, uint32_t *i)
|
||||
{
|
||||
char c;
|
||||
UINT bytes;
|
||||
FRESULT res = f_read(fp, &c, 1, &bytes);
|
||||
if (res != FR_OK || bytes != 1) {
|
||||
c = EOF;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static DISABLE_OPT int read_num(FIL *fp)
|
||||
{
|
||||
int x=0, c=0;
|
||||
while (1) {
|
||||
c = f_getc(fp);
|
||||
if (c == '#') {
|
||||
/* skip comments */
|
||||
while ((c = f_getc(fp)) != EOF && c != '\n');
|
||||
} else if (c >= 48 && c <= 57) {
|
||||
x *= 10;
|
||||
x += (c-48);
|
||||
} else {
|
||||
break;
|
||||
enum { EAT_WHITESPACE, EAT_COMMENT, EAT_NUMBER } mode = EAT_WHITESPACE;
|
||||
for(*i = 0;;) {
|
||||
if (!read_int_c_valid) {
|
||||
if (f_eof(fp)) {
|
||||
return FR_OK;
|
||||
}
|
||||
int res = read_byte(fp, &read_int_c);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
read_int_c_valid = true;
|
||||
}
|
||||
if (mode == EAT_WHITESPACE) {
|
||||
if (read_int_c == '#') {
|
||||
mode = EAT_COMMENT;
|
||||
} else if (('0' <= read_int_c) && (read_int_c <= '9')) {
|
||||
*i = read_int_c - '0';
|
||||
mode = EAT_NUMBER;
|
||||
}
|
||||
} else if (mode == EAT_COMMENT) {
|
||||
if ((read_int_c == '\n') || (read_int_c == '\r')) {
|
||||
mode = EAT_WHITESPACE;
|
||||
}
|
||||
} else if (mode == EAT_NUMBER) {
|
||||
if (('0' <= read_int_c) && (read_int_c <= '9')) {
|
||||
*i = (*i * 10) + read_int_c - '0';
|
||||
} else {
|
||||
return FR_OK; // read_int_c_valid==true on exit
|
||||
}
|
||||
}
|
||||
read_int_c_valid = false;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int ppm_write(image_t *img, const char *path)
|
||||
#define READ_INT(fp, i) \
|
||||
({ FRESULT _res = read_int((fp), (i)); \
|
||||
if (_res != FR_OK) { f_close((fp)); return _res; } })
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Store mode settings internally, no need for callers to store this info.
|
||||
static uint8_t ppm_fmt;
|
||||
|
||||
// This function inits the geometry values of an image.
|
||||
int ppm_read_geometry(FIL *fp, image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
UINT bytes;
|
||||
FRESULT res=FR_OK;
|
||||
|
||||
int idx=0;
|
||||
uint8_t buf[BUF_LEN];
|
||||
|
||||
res = f_open(&fp, path, FA_WRITE|FA_CREATE_ALWAYS);
|
||||
read_int_reset();
|
||||
FRESULT res = f_open(fp, path, FA_READ|FA_OPEN_EXISTING);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (img->bpp == 1) {
|
||||
bytes = f_printf(&fp, "P5\n%d %d\n255\n", img->w, img->h);
|
||||
} else {
|
||||
bytes = f_printf(&fp, "P6\n%d %d\n255\n", img->w, img->h);
|
||||
READ_BYTE_EXPECT(fp, 'P');
|
||||
READ_BYTE(fp, &ppm_fmt);
|
||||
if ((ppm_fmt!='2') && (ppm_fmt!='3') && (ppm_fmt!='5') && (ppm_fmt!='6')) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
img->bpp = ((ppm_fmt == '2') || (ppm_fmt == '5')) ? 1 : 2;
|
||||
|
||||
READ_INT(fp, (uint32_t *) &img->w);
|
||||
READ_INT(fp, (uint32_t *) &img->h);
|
||||
if ((img->w == 0) || (img->h == 0)) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bytes == -1) {
|
||||
res = FR_DENIED;
|
||||
goto error;
|
||||
uint32_t max;
|
||||
READ_INT(fp, &max);
|
||||
if (max != 255) {
|
||||
f_close(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (img->bpp == 1) {
|
||||
res = f_write(&fp, img->data, img->w*img->h, &bytes);
|
||||
} else {
|
||||
uint16_t *pixels = (uint16_t*)img->data;
|
||||
for (int i=0; i<img->w*img->h; i++) {
|
||||
uint16_t p = pixels[i];
|
||||
buf[idx++]=R8(p); buf[idx++]=G8(p); buf[idx++]=B8(p); //RGB565->RGB888
|
||||
if (idx == BUF_LEN) {
|
||||
idx = 0;
|
||||
res = f_write(&fp, buf, BUF_LEN, &bytes);
|
||||
if (res != FR_OK || bytes != BUF_LEN) {
|
||||
goto error;
|
||||
}
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
// This function reads the pixel values of an image.
|
||||
int ppm_read_pixels(FIL *fp, image_t *img, int line_start, int line_end)
|
||||
{
|
||||
if (ppm_fmt == '2') {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0; j < img->w; j++) {
|
||||
uint32_t pixel;
|
||||
READ_INT(fp, &pixel);
|
||||
IM_SET_GS_PIXEL(img, j, i, pixel);
|
||||
}
|
||||
}
|
||||
if (idx) {
|
||||
res = f_write(&fp, buf, idx, &bytes);
|
||||
if (res != FR_OK || bytes != idx) {
|
||||
goto error;
|
||||
} else if (ppm_fmt == '3') {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0; j < img->w; j++) {
|
||||
uint32_t r, g, b;
|
||||
READ_INT(fp, &r);
|
||||
READ_INT(fp, &g);
|
||||
READ_INT(fp, &b);
|
||||
IM_SET_RGB565_PIXEL(img, j, i, IM_RGB565(IM_R825(r),
|
||||
IM_G826(g),
|
||||
IM_B825(b)));
|
||||
}
|
||||
}
|
||||
} else if (ppm_fmt == '5') {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0; j < img->w; j++) {
|
||||
uint8_t pixel;
|
||||
READ_BYTE(fp, &pixel);
|
||||
IM_SET_GS_PIXEL(img, j, i, pixel);
|
||||
}
|
||||
}
|
||||
} else if (ppm_fmt == '6') {
|
||||
for (int i = line_start; i < line_end; i++) {
|
||||
for (int j = 0; j < img->w; j++) {
|
||||
uint8_t r, g, b;
|
||||
READ_BYTE(fp, &r);
|
||||
READ_BYTE(fp, &g);
|
||||
READ_BYTE(fp, &b);
|
||||
IM_SET_RGB565_PIXEL(img, j, i, IM_RGB565(IM_R825(r),
|
||||
IM_G826(g),
|
||||
IM_B825(b)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
f_close(&fp);
|
||||
return res;
|
||||
return FR_OK;
|
||||
}
|
||||
|
||||
static int ppm_read_int(image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
|
||||
int res = ppm_read_geometry(&fp, img, path);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!img->pixels) { // don't allocate if already allocated...
|
||||
img->pixels = xalloc(img->w * img->h * img->bpp);
|
||||
}
|
||||
|
||||
res = ppm_read_pixels(&fp, img, 0, img->h);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return f_close(&fp);
|
||||
}
|
||||
|
||||
int ppm_read(image_t *img, const char *path)
|
||||
{
|
||||
FIL fp;
|
||||
UINT bytes;
|
||||
FRESULT res=FR_OK;
|
||||
|
||||
res = f_open(&fp, path, FA_READ|FA_OPEN_EXISTING);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
const uint8_t *backup = img->pixels;
|
||||
const int res = ppm_read_int(img, path);
|
||||
// free image if I didn't start with one...
|
||||
if ((res != FR_OK) && (!backup) && (img->pixels)) {
|
||||
xfree(img->pixels);
|
||||
}
|
||||
|
||||
/* read image header */
|
||||
if (f_getc(&fp) != 'P') {
|
||||
printf("ppm:image format not supported\n");
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
int type = f_getc(&fp);
|
||||
if (type != '5' && type != '6') {
|
||||
printf("ppm:image format not supported\n");
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!isspace(f_getc(&fp))) {
|
||||
printf("ppm:image format not supported\n");
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
img->w = read_num(&fp); /* read image width */
|
||||
img->h = read_num(&fp); /* read image height */
|
||||
int max_gray = read_num(&fp); /* read image max gray */
|
||||
if (img->w == -1 || img->h == -1 || max_gray != 255) {
|
||||
printf("ppm:image format not supported\n");
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
img->bpp = (type=='5')? 1:2;
|
||||
int size = img->w*img->h*img->bpp;
|
||||
|
||||
/* alloc image */
|
||||
img->data = xalloc(size);
|
||||
if (img->data == NULL) {
|
||||
printf("ppm: out of memory\n");
|
||||
res = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* read image data */
|
||||
res = f_read(&fp, img->data, size, &bytes);
|
||||
if (res != FR_OK || bytes != size) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
f_close(&fp);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ppm_write_subimg(image_t *img, const char *path, rectangle_t *r)
|
||||
{
|
||||
rectangle_t rect;
|
||||
if (!rectangle_subimg(img, r, &rect)) {
|
||||
return -1; // no image intersection
|
||||
}
|
||||
|
||||
FIL fp;
|
||||
UINT bytes;
|
||||
FRESULT res=FR_OK;
|
||||
|
||||
int idx=0;
|
||||
uint8_t buf[BUF_LEN];
|
||||
|
||||
res = f_open(&fp, path, FA_WRITE|FA_CREATE_ALWAYS);
|
||||
FRESULT res = f_open(&fp, path, FA_WRITE|FA_CREATE_ALWAYS);
|
||||
if (res != FR_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (img->bpp == 1) {
|
||||
bytes = f_printf(&fp, "P5\n%d %d\n255\n", r->w, r->h);
|
||||
} else {
|
||||
bytes = f_printf(&fp, "P6\n%d %d\n255\n", r->w, r->h);
|
||||
}
|
||||
|
||||
if (bytes == -1) {
|
||||
res = FR_DENIED;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (img->bpp == 1) {
|
||||
for (int j=r->y; j<r->y+r->h; j++) {
|
||||
for (int i=r->x; i<r->x+r->w; i++) {
|
||||
uint8_t c = img->pixels[j*img->w+i];
|
||||
res = f_write(&fp, &c, 1, &bytes);
|
||||
if (res != FR_OK || bytes != 1) {
|
||||
goto error;
|
||||
}
|
||||
if (IM_IS_GS(img)) {
|
||||
char buffer[20]; // exactly big enough for 5-digit w/h
|
||||
int len = snprintf(buffer, 20, "P5\n%d %d\n255\n", rect.w, rect.h);
|
||||
WRITE_DATA(&fp, buffer, len);
|
||||
for (int i = 0; i < rect.h; i++) {
|
||||
for (int j = 0; j < rect.w; j++) {
|
||||
WRITE_BYTE(&fp, IM_GET_GS_PIXEL(img, (rect.x + j), (rect.y + i)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uint16_t *pixels = (uint16_t*)img->data;
|
||||
for (int j=r->y; j<r->y+r->h; j++) {
|
||||
for (int i=r->x; i<r->x+r->w; i++) {
|
||||
uint16_t p = pixels[j*img->w+i];
|
||||
buf[idx++]=R8(p); buf[idx++]=G8(p); buf[idx++]=B8(p); //RGB565->RGB888
|
||||
if (idx == BUF_LEN) {
|
||||
idx = 0;
|
||||
res = f_write(&fp, buf, BUF_LEN, &bytes);
|
||||
if (res != FR_OK || bytes != BUF_LEN) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (idx) {
|
||||
res = f_write(&fp, buf, idx, &bytes);
|
||||
if (res != FR_OK || bytes != idx) {
|
||||
goto error;
|
||||
char buffer[20]; // exactly big enough for 5-digit w/h
|
||||
int len = snprintf(buffer, 20, "P6\n%d %d\n255\n", rect.w, rect.h);
|
||||
WRITE_DATA(&fp, buffer, len);
|
||||
for (int i = 0; i < rect.h; i++) {
|
||||
for (int j = 0; j < rect.w; j++) {
|
||||
int pixel = IM_GET_RGB565_PIXEL(img, (rect.x + j), (rect.y + i));
|
||||
char buff[3];
|
||||
buff[0] = IM_R528(IM_R565(pixel));
|
||||
buff[1] = IM_G628(IM_G565(pixel));
|
||||
buff[2] = IM_B528(IM_B565(pixel));
|
||||
WRITE_DATA(&fp, buff, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
f_close(&fp);
|
||||
return res;
|
||||
return f_close(&fp);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user