* add `eb_put_filename()` to encode buffer or file names with non-ASCII characters according to PF_xxx flags * add `CF_SAVE_LIST` flag for completions to store the list of matches in `MinibufState` * fix bufed and dired layout issues * fix buffer and filename completion popup layout * add commands to control display of Unicode and non printing charcaters in buffer or file names: - U toggles unicode display - \ cycles through 4 possible encodings: ? ^J \x10 \010 * use `(*fp)()` syntax for indirect function calls
934 lines
23 KiB
C
934 lines
23 KiB
C
/*
|
|
* Image mode for QEmacs.
|
|
*
|
|
* Copyright (c) 2002-2003 Fabrice Bellard.
|
|
* Copyright (c) 2003-2024 Charlie Gordon.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "qe.h"
|
|
#include "avformat.h"
|
|
|
|
#define SCROLL_MHEIGHT 10
|
|
|
|
typedef struct ImageBuffer ImageBuffer;
|
|
typedef struct ImageState ImageState;
|
|
typedef struct ImageBufferState ImageBufferState;
|
|
|
|
struct ImageBufferState {
|
|
QEModeData base;
|
|
ImageBuffer *ib;
|
|
};
|
|
|
|
struct ImageBuffer {
|
|
int pix_fmt;
|
|
int width;
|
|
int height;
|
|
int interleaved; /* just an info to tell if the image is interleaved */
|
|
int alpha_info; /* see FF_ALPHA_xxx constants */
|
|
AVPicture pict; /* NOTE: data[] fields are temporary */
|
|
};
|
|
|
|
struct ImageState {
|
|
QEModeData base;
|
|
ImageBufferState *ibs;
|
|
QEBitmap *disp_bmp;
|
|
int x, y; /* position of the center of the image in window */
|
|
int w, h; /* displayed size */
|
|
int xfactor_num, xfactor_den;
|
|
int yfactor_num, yfactor_den;
|
|
/* XXX: should also have a current zone with mark(x/y) and cur(x/y) */
|
|
QEColor background_color; /* transparent to display tiles */
|
|
};
|
|
|
|
int qe_bitmap_format_to_pix_fmt(int format)
|
|
{
|
|
int dst_pix_fmt;
|
|
|
|
switch (format) {
|
|
case QEBITMAP_FORMAT_YUV420P:
|
|
dst_pix_fmt = PIX_FMT_YUV420P;
|
|
break;
|
|
case QEBITMAP_FORMAT_RGB555:
|
|
dst_pix_fmt = PIX_FMT_RGB555;
|
|
break;
|
|
default:
|
|
case QEBITMAP_FORMAT_RGB565:
|
|
dst_pix_fmt = PIX_FMT_RGB565;
|
|
break;
|
|
case QEBITMAP_FORMAT_RGB24:
|
|
dst_pix_fmt = PIX_FMT_RGB24;
|
|
break;
|
|
case QEBITMAP_FORMAT_RGBA32:
|
|
dst_pix_fmt = PIX_FMT_RGBA32;
|
|
break;
|
|
}
|
|
return dst_pix_fmt;
|
|
}
|
|
|
|
static void image_callback(EditBuffer *b, void *opaque, int arg,
|
|
enum LogOperation op, int offset, int size);
|
|
|
|
void draw_alpha_grid(EditState *s, int x1, int y1, int w, int h)
|
|
{
|
|
int state, x, y;
|
|
unsigned int color;
|
|
|
|
for (y = 0; y < h; y += 16) {
|
|
for (x = 0; x < w; x += 16) {
|
|
state = (x ^ y) & 16;
|
|
if (state)
|
|
color = QERGB(0x94, 0x94, 0x94);
|
|
else
|
|
color = QERGB(0x64, 0x64, 0x64);
|
|
fill_rectangle(s->screen,
|
|
x1 + x, y1 + y, 16, 16, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
static ImageState *image_get_state(EditState *e, int status)
|
|
{
|
|
return qe_get_window_mode_data(e, &image_mode, status);
|
|
}
|
|
|
|
/* transp: 0x94 and 0x64, 16x16 grid */
|
|
|
|
static void image_display(EditState *s)
|
|
{
|
|
ImageState *is = image_get_state(s, 0);
|
|
int x, y;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
if (s->display_invalid) {
|
|
if (is->disp_bmp) {
|
|
x = is->x + (s->width - is->w) / 2;
|
|
y = is->y + (s->height - is->h) / 2;
|
|
|
|
fill_window_slack(s, x, y, is->w, is->h, QERGB(0x00, 0x00, 0x00));
|
|
|
|
bmp_draw(s->screen, is->disp_bmp,
|
|
s->xleft + x, s->ytop + y,
|
|
is->w, is->h, 0, 0, 0);
|
|
}
|
|
s->display_invalid = 0;
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
|
|
static int gcd(int a, int b)
|
|
{
|
|
int c;
|
|
|
|
while (1) {
|
|
c = a % b;
|
|
if (c == 0)
|
|
return b;
|
|
a = b;
|
|
b = c;
|
|
}
|
|
}
|
|
|
|
|
|
/* resize current image using the current factors */
|
|
static void image_resize(EditState *s)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
ImageBuffer *ib;
|
|
int d, w, h;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ibs->ib;
|
|
|
|
/* simplify factors */
|
|
d = gcd(is->xfactor_num, is->xfactor_den);
|
|
is->xfactor_num /= d;
|
|
is->xfactor_den /= d;
|
|
|
|
d = gcd(is->yfactor_num, is->yfactor_den);
|
|
is->yfactor_num /= d;
|
|
is->yfactor_den /= d;
|
|
|
|
w = ((long long)ib->width * (long long)is->xfactor_num) /
|
|
is->xfactor_den;
|
|
h = ((long long)ib->height * (long long)is->yfactor_num) /
|
|
is->yfactor_den;
|
|
|
|
if (w < 1)
|
|
w = 1;
|
|
if (h < 1)
|
|
h = 1;
|
|
|
|
/* if no resize needed, exit */
|
|
if (w == is->w && h == is->h)
|
|
return;
|
|
|
|
edit_invalidate(s, 0);
|
|
}
|
|
|
|
static void image_normal_size(EditState *s)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
is->xfactor_num = 1;
|
|
is->xfactor_den = 1;
|
|
is->yfactor_num = 1;
|
|
is->yfactor_den = 1;
|
|
|
|
image_resize(s);
|
|
}
|
|
|
|
|
|
/* increase or decrease image size by percent */
|
|
static void image_mult_size(EditState *s, int percent)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
is->xfactor_num *= (100 + percent);
|
|
is->xfactor_den *= 100;
|
|
is->yfactor_num *= (100 + percent);
|
|
is->yfactor_den *= 100;
|
|
|
|
image_resize(s);
|
|
}
|
|
|
|
static void image_set_size(EditState *s, int w, int h)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
ImageBuffer *ib;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ibs->ib;
|
|
|
|
if (w < 1 || h < 1) {
|
|
put_error(s, "Invalid image size");
|
|
return;
|
|
}
|
|
|
|
is->xfactor_num = w;
|
|
is->xfactor_den = ib->width;
|
|
is->yfactor_num = h;
|
|
is->yfactor_den = ib->height;
|
|
|
|
image_resize(s);
|
|
}
|
|
#endif
|
|
|
|
static int image_mode_probe(ModeDef *mode, ModeProbeData *pd)
|
|
{
|
|
AVProbeData avpd;
|
|
AVImageFormat *fmt;
|
|
|
|
avpd.filename = pd->filename;
|
|
avpd.buf = pd->buf;
|
|
avpd.buf_size = pd->buf_size;
|
|
fmt = av_probe_image_format(&avpd);
|
|
if (!fmt)
|
|
return 0;
|
|
else
|
|
return 100;
|
|
}
|
|
|
|
static void image_mode_free(EditBuffer *b, void *state)
|
|
{
|
|
ImageBufferState *ibs = state;
|
|
|
|
image_free(&ibs->ib);
|
|
}
|
|
|
|
/* allocate a new image at the end of the buffer */
|
|
static ImageBuffer *image_allocate(int pix_fmt, int width, int height)
|
|
{
|
|
unsigned char *ptr;
|
|
int size;
|
|
ImageBuffer *ib;
|
|
|
|
ib = qe_mallocz(ImageBuffer);
|
|
if (!ib)
|
|
return NULL;
|
|
|
|
size = avpicture_get_size(pix_fmt, width, height);
|
|
if (size < 0)
|
|
goto fail;
|
|
ptr = qe_malloc_array(u8, size);
|
|
if (!ptr) {
|
|
fail:
|
|
qe_free(&ib);
|
|
return NULL;
|
|
}
|
|
|
|
avpicture_fill(&ib->pict, ptr, pix_fmt, width, height);
|
|
ib->pix_fmt = pix_fmt;
|
|
ib->width = width;
|
|
ib->height = height;
|
|
return ib;
|
|
}
|
|
|
|
static void image_free(ImageBuffer **ibp)
|
|
{
|
|
if (*ibp) {
|
|
qe_free(&(*ibp)->pict.data[0]);
|
|
qe_free(ibp);
|
|
}
|
|
}
|
|
|
|
static int read_image_cb(void *opaque, AVImageInfo *info)
|
|
{
|
|
ImageBufferState *ibs = opaque;
|
|
ImageBuffer *ib;
|
|
int i;
|
|
|
|
ib = image_allocate(info->pix_fmt, info->width, info->height);
|
|
if (!ib)
|
|
return AVERROR_NOMEM;
|
|
|
|
ibs->ib = ib;
|
|
ib->interleaved = info->interleaved;
|
|
for (i = 0; i < 4; i++) {
|
|
info->pict.linesize[i] = ib->pict.linesize[i];
|
|
info->pict.data[i] = ib->pict.data[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int image_buffer_load(EditBuffer *b, FILE *f)
|
|
{
|
|
ByteIOContext pb1, *pb = &pb1;
|
|
ImageBufferState *ibs;
|
|
int ret;
|
|
|
|
ibs = qe_get_buffer_mode_data(b, &image_mode, NULL);
|
|
if (!ibs)
|
|
return;
|
|
|
|
/* start loading the image */
|
|
ret = url_fopen(pb, b->filename, URL_RDONLY);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
ret = av_read_image(pb, b->filename, NULL, read_image_cb, ibs);
|
|
url_fclose(pb);
|
|
if (ret) {
|
|
return -1;
|
|
} else {
|
|
ImageBuffer *ib = ibs->ib;
|
|
ib->alpha_info = img_get_alpha_info(&ib->pict, ib->pix_fmt,
|
|
ib->width, ib->height);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int set_new_image(EditBuffer *b, ImageBuffer *ib)
|
|
{
|
|
ImageBufferState *ibs = qe_get_buffer_mode_data(b, &image_mode, NULL);
|
|
|
|
if (!ibs)
|
|
return -1;
|
|
|
|
image_free(&ibs->ib);
|
|
ibs->ib = ib;
|
|
/* XXX: should signal all windows that image changed? */
|
|
eb_invalidate_raw_data(b);
|
|
b->modified = 1; /* not really */
|
|
return 0;
|
|
}
|
|
|
|
static int image_buffer_save(EditBuffer *b, int start, int end,
|
|
const char *filename)
|
|
{
|
|
ByteIOContext pb1, *pb = &pb1;
|
|
ImageBufferState *ibs = qe_get_buffer_mode_data(b, &image_mode, NULL);
|
|
ImageBuffer *ib;
|
|
ImageBuffer *ib1 = NULL;
|
|
int ret, dst_pix_fmt, loss;
|
|
AVImageFormat *fmt;
|
|
AVImageInfo info;
|
|
|
|
if (!ibs || !ibs->ib)
|
|
return -1;
|
|
|
|
ib = ibs->ib;
|
|
|
|
/* find image format */
|
|
fmt = guess_image_format(filename);
|
|
if (!fmt)
|
|
return -1;
|
|
|
|
/* find the best image format */
|
|
dst_pix_fmt = avcodec_find_best_pix_fmt(fmt->supported_pixel_formats,
|
|
ib->pix_fmt, ib->alpha_info, &loss);
|
|
if (dst_pix_fmt < 0)
|
|
return -1;
|
|
|
|
/* convert to new format if needed */
|
|
if (dst_pix_fmt != ib->pix_fmt) {
|
|
ib1 = image_allocate(dst_pix_fmt, ib->width, ib->height);
|
|
if (!ib1)
|
|
return -1;
|
|
if (img_convert(&ib1->pict, ib1->pix_fmt,
|
|
&ib->pict, ib->pix_fmt, ib->width, ib->height) < 0) {
|
|
image_free(&ib1);
|
|
return -1;
|
|
}
|
|
set_new_image(b, ib1);
|
|
ib = ib1;
|
|
}
|
|
|
|
/* start saving the image */
|
|
ret = url_fopen(pb, filename, URL_WRONLY);
|
|
if (ret < 0)
|
|
return -1;
|
|
memset(&info, 0, sizeof(AVImageInfo));
|
|
info.pix_fmt = ib->pix_fmt;
|
|
info.width = ib->width;
|
|
info.height = ib->height;
|
|
info.pict = ib->pict;
|
|
|
|
av_write_image(pb, fmt, &info);
|
|
url_fclose(pb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void image_buffer_close(EditBuffer *b)
|
|
{
|
|
ImageBufferState *ibs = qe_get_buffer_mode_data(b, &image_mode, NULL);
|
|
|
|
if (ibs) {
|
|
image_free(&ibs->ib);
|
|
}
|
|
}
|
|
|
|
static void update_bmp(EditState *s)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
ImageBuffer *ib;
|
|
QEPicture pict;
|
|
AVPicture avpict;
|
|
ImageBuffer *ib1;
|
|
int dst_pix_fmt;
|
|
int i;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ibs->ib;
|
|
|
|
bmp_free(s->screen, &is->disp_bmp);
|
|
|
|
/* combine with the appropriate background if alpha is present */
|
|
ib1 = NULL;
|
|
if (ib->alpha_info) {
|
|
int x, y, state, linesize;
|
|
unsigned int r, g, b, a, bg_r, bg_g, bg_b, v;
|
|
uint8_t *d, *d1;
|
|
|
|
ib1 = image_allocate(PIX_FMT_RGBA32, is->w, is->h);
|
|
if (!ib1)
|
|
goto next;
|
|
img_convert(&ib1->pict, ib1->pix_fmt,
|
|
&ib->pict, ib->pix_fmt, ib->width, ib->height);
|
|
linesize = ib1->pict.linesize[0];
|
|
d1 = ib1->pict.data[0];
|
|
bg_r = (is->background_color >> 16) & 0xff;
|
|
bg_g = (is->background_color >> 8) & 0xff;
|
|
bg_b = (is->background_color) & 0xff;
|
|
for (y = 0; y < is->h; y++) {
|
|
d = d1;
|
|
for (x = 0; x < is->w; x++) {
|
|
if (is->background_color == 0) {
|
|
state = (x ^ y) & 16;
|
|
if (state) {
|
|
bg_r = bg_g = bg_b = 0x94;
|
|
} else {
|
|
bg_r = bg_g = bg_b = 0x64;
|
|
}
|
|
}
|
|
v = ((uint32_t *)d)[0];
|
|
a = (v >> 24) & 0xff;
|
|
r = (v >> 16) & 0xff;
|
|
g = (v >> 8) & 0xff;
|
|
b = (v) & 0xff;
|
|
r = (bg_r * (256 - a) + r * a) >> 8;
|
|
g = (bg_g * (256 - a) + g * a) >> 8;
|
|
b = (bg_b * (256 - a) + b * a) >> 8;
|
|
((uint32_t *)d)[0] = (0xff << 24) | (r << 16) | (g << 8) | b;
|
|
d += 4;
|
|
}
|
|
d1 += linesize;
|
|
}
|
|
ib = ib1;
|
|
next: ;
|
|
}
|
|
|
|
/* create the displayed bitmap and put the image in it */
|
|
is->disp_bmp = bmp_alloc(s->screen, is->w, is->h, 0);
|
|
|
|
bmp_lock(s->screen, is->disp_bmp, &pict,
|
|
0, 0, is->w, is->h);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
avpict.data[i] = pict.data[i];
|
|
avpict.linesize[i] = pict.linesize[i];
|
|
}
|
|
dst_pix_fmt = qe_bitmap_format_to_pix_fmt(is->disp_bmp->format);
|
|
#if 0
|
|
printf("dst=%s src=%s\n",
|
|
avcodec_get_pix_fmt_name(dst_pix_fmt),
|
|
avcodec_get_pix_fmt_name(ib->pix_fmt));
|
|
#endif
|
|
if (img_convert(&avpict, dst_pix_fmt,
|
|
&ib->pict, ib->pix_fmt, ib->width, ib->height) < 0) {
|
|
printf("Cannot convert from %s to %s\n",
|
|
avcodec_get_pix_fmt_name(ib->pix_fmt),
|
|
avcodec_get_pix_fmt_name(dst_pix_fmt));
|
|
}
|
|
bmp_unlock(s->screen, is->disp_bmp);
|
|
if (ib1)
|
|
image_free(ib1);
|
|
edit_invalidate(s, 0);
|
|
}
|
|
|
|
static int image_mode_init(EditState *s, EditBuffer *b, int flags)
|
|
{
|
|
if (s) {
|
|
if (flags & MODEF_NEWINSTANCE) {
|
|
ImageState *is = qe_get_window_mode_data(s, &image_mode, 0);
|
|
ImageBufferState *ibs = qe_get_buffer_mode_data(b, &image_mode, NULL);
|
|
ImageBuffer *ib;
|
|
|
|
if (!ibs || !is)
|
|
return -1;
|
|
|
|
ib = qe_mallocz(ImageBuffer);
|
|
if (!ib)
|
|
return -1;
|
|
|
|
is->ibs = ibs;
|
|
ibs->ib = ib;
|
|
is->w = ib->width;
|
|
is->h = ib->height;
|
|
is->xfactor_num = 1;
|
|
is->xfactor_den = 1;
|
|
is->yfactor_num = 1;
|
|
is->yfactor_den = 1;
|
|
is->background_color = 0; /* display tiles */
|
|
}
|
|
update_bmp(s);
|
|
|
|
eb_add_callback(s->b, image_callback, s, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void update_pos(EditState *s, int dx, int dy)
|
|
{
|
|
ImageState *is = image_get_state(s, 1);
|
|
int delta;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
is->x += dx;
|
|
delta = (s->width - is->w) / 2;
|
|
if (delta < 0) {
|
|
if (is->x + delta > 0)
|
|
is->x = -delta;
|
|
else if (is->x + delta + is->w < s->width)
|
|
is->x = s->width - is->w - delta;
|
|
} else {
|
|
is->x = 0;
|
|
}
|
|
|
|
is->y += dy;
|
|
delta = (s->height - is->h) / 2;
|
|
if (delta < 0) {
|
|
if (is->y + delta > 0)
|
|
is->y = -delta;
|
|
else if (is->y + delta + is->h < s->height)
|
|
is->y = s->height - is->h - delta;
|
|
} else {
|
|
is->y = 0;
|
|
}
|
|
edit_invalidate(s, 0);
|
|
}
|
|
|
|
static void image_move_left_right(EditState *s, int disp)
|
|
{
|
|
int d;
|
|
|
|
/* move 10% */
|
|
d = s->width / 10;
|
|
if (d < 1)
|
|
d = 1;
|
|
update_pos(s, -disp * d, 0);
|
|
}
|
|
|
|
static void image_move_up_down(EditState *s, int disp)
|
|
{
|
|
int d;
|
|
|
|
/* move 10% */
|
|
d = s->height / 10;
|
|
if (d < 1)
|
|
d = 1;
|
|
update_pos(s, 0, -disp * d);
|
|
}
|
|
|
|
static void image_scroll_up_down(EditState *s, int dir)
|
|
{
|
|
int d;
|
|
|
|
d = SCROLL_MHEIGHT;
|
|
if (abs(dir) == 2) {
|
|
/* move 50% */
|
|
d = s->height / 2;
|
|
dir /= 2;
|
|
}
|
|
if (d < 1)
|
|
d = 1;
|
|
update_pos(s, 0, -dir * d);
|
|
}
|
|
|
|
static void image_mode_close(EditState *s)
|
|
{
|
|
ImageState *is = image_get_state(s, 0);
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
bmp_free(s->screen, &is->disp_bmp);
|
|
eb_free_callback(s->b, image_callback, s);
|
|
}
|
|
|
|
/* when the image is modified, reparse it */
|
|
static void image_callback(EditBuffer *b, void *opaque, int arg,
|
|
enum LogOperation op, int offset, int size)
|
|
{
|
|
// EditState *s = opaque;
|
|
|
|
// update_bmp(s);
|
|
}
|
|
|
|
static int img_rotate(AVPicture *dst,
|
|
AVPicture *src, int pix_fmt,
|
|
int w, int h)
|
|
{
|
|
int x, y, dlinesize, bpp;
|
|
uint8_t *d, *d1;
|
|
const uint8_t *s, *s1;
|
|
|
|
switch (pix_fmt) {
|
|
case PIX_FMT_GRAY8:
|
|
case PIX_FMT_PAL8:
|
|
bpp = 1;
|
|
break;
|
|
case PIX_FMT_RGB24:
|
|
case PIX_FMT_BGR24:
|
|
bpp = 3;
|
|
break;
|
|
case PIX_FMT_RGBA32:
|
|
bpp = 4;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
s1 = src->data[0];
|
|
dlinesize = dst->linesize[0];
|
|
d1 = dst->data[0] + (h - 1) * bpp;
|
|
|
|
for (y = 0; y < h; y++) {
|
|
s = s1;
|
|
d = d1;
|
|
|
|
switch (pix_fmt) {
|
|
case PIX_FMT_PAL8:
|
|
case PIX_FMT_GRAY8:
|
|
for (x = 0; x < w; x++) {
|
|
d[0] = s[0];
|
|
s++;
|
|
d += dlinesize;
|
|
}
|
|
break;
|
|
case PIX_FMT_RGB24:
|
|
case PIX_FMT_BGR24:
|
|
for (x = 0; x < w; x++) {
|
|
d[0] = s[0];
|
|
d[1] = s[1];
|
|
d[2] = s[2];
|
|
s += 3;
|
|
d += dlinesize;
|
|
}
|
|
break;
|
|
case PIX_FMT_RGBA32:
|
|
for (x = 0; x < w; x++) {
|
|
((uint32_t *)d)[0] = ((uint32_t *)s)[0];
|
|
s += 4;
|
|
d += dlinesize;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
s1 += src->linesize[0];
|
|
d1 -= bpp;
|
|
}
|
|
|
|
if (pix_fmt == PIX_FMT_PAL8) {
|
|
/* copy palette */
|
|
memcpy(dst->data[1], src->data[1], 256 * sizeof(uint32_t));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void image_rotate(EditState *e)
|
|
{
|
|
ImageState *is = image_get_state(e, 1);
|
|
ImageBuffer *ib;
|
|
int ret, w, h, pix_fmt;
|
|
ImageBuffer *ib1;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ibs->ib;
|
|
|
|
pix_fmt = ib->pix_fmt;
|
|
w = ib->width;
|
|
h = ib->height;
|
|
ib1 = image_allocate(pix_fmt, h, w);
|
|
if (!ib1)
|
|
return;
|
|
|
|
/* need to reget image because buffer address may have changed */
|
|
ret = img_rotate(&ib1->pict, &ib->pict, pix_fmt, w, h);
|
|
if (ret < 0) {
|
|
/* remove temporary image */
|
|
image_free(ib1);
|
|
put_error(e, "Format '%s' not supported yet in rotate",
|
|
avcodec_get_pix_fmt_name(pix_fmt));
|
|
return;
|
|
}
|
|
ib1->alpha_info = ib->alpha_info;
|
|
set_new_image(e->b, ib1);
|
|
/* temporary */
|
|
is->w = h;
|
|
is->h = w;
|
|
/* suppress that and use callback */
|
|
update_bmp(e);
|
|
}
|
|
|
|
static void image_set_background_color(EditState *e, const char *color_str)
|
|
{
|
|
ImageState *is = image_get_state(s, 0);
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
css_get_color(&is->background_color, color_str);
|
|
update_bmp(e);
|
|
}
|
|
|
|
static void image_convert(EditState *e, const char *pix_fmt_str)
|
|
{
|
|
ImageState *is = image_get_state(s, 0);
|
|
ImageBuffer *ib;
|
|
int ret, new_pix_fmt, i, loss;
|
|
ImageBuffer *ib1;
|
|
const char *name;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ibs->ib;
|
|
|
|
for (i = 0; i < PIX_FMT_NB; i++) {
|
|
name = avcodec_get_pix_fmt_name(i);
|
|
if (strequal(name, pix_fmt_str))
|
|
goto found;
|
|
}
|
|
put_error(e, "Unknown pixel format");
|
|
return;
|
|
found:
|
|
new_pix_fmt = i;
|
|
ib1 = image_allocate(new_pix_fmt, ib->width, ib->height);
|
|
if (!ib1)
|
|
return;
|
|
ret = img_convert(&ib1->pict, ib1->pix_fmt,
|
|
&ib->pict, ib->pix_fmt, ib->width, ib->height);
|
|
if (ret < 0) {
|
|
/* remove temporary image */
|
|
image_free(ib1);
|
|
put_error(e, "Conversion from '%s' to '%s' not supported yet",
|
|
avcodec_get_pix_fmt_name(ib->pix_fmt),
|
|
avcodec_get_pix_fmt_name(new_pix_fmt));
|
|
return;
|
|
} else {
|
|
char buf[128];
|
|
loss = avcodec_get_pix_fmt_loss(new_pix_fmt, ib->pix_fmt, ib->alpha_info);
|
|
if (loss != 0) {
|
|
buf[0] = '\0';
|
|
if (loss & FF_LOSS_RESOLUTION)
|
|
pstrcat(buf, sizeof(buf), " res");
|
|
if (loss & FF_LOSS_DEPTH)
|
|
pstrcat(buf, sizeof(buf), " depth");
|
|
if (loss & FF_LOSS_COLORSPACE)
|
|
pstrcat(buf, sizeof(buf), " colorspace");
|
|
if (loss & FF_LOSS_ALPHA)
|
|
pstrcat(buf, sizeof(buf), " alpha");
|
|
if (loss & FF_LOSS_COLORQUANT)
|
|
pstrcat(buf, sizeof(buf), " colorquant");
|
|
if (loss & FF_LOSS_CHROMA)
|
|
pstrcat(buf, sizeof(buf), " chroma");
|
|
put_status(e, "Warning: data loss:%s", buf);
|
|
}
|
|
}
|
|
ib1->alpha_info = img_get_alpha_info(&ib1->pict, ib1->pix_fmt,
|
|
ib1->width, ib1->height);
|
|
set_new_image(e->b, ib1);
|
|
/* suppress that and use callback */
|
|
update_bmp(e);
|
|
}
|
|
|
|
void image_mode_line(EditState *s, buf_t *out)
|
|
{
|
|
EditBuffer *b = s->b;
|
|
ImageState *is = image_get_state(s, 0);
|
|
ImageBuffer *ib;
|
|
char alpha_mode;
|
|
|
|
if (!is)
|
|
return;
|
|
|
|
ib = is->ib;
|
|
|
|
basic_mode_line(s, out, '-');
|
|
|
|
if (ib->alpha_info & FF_ALPHA_SEMI_TRANSP)
|
|
alpha_mode = 'A';
|
|
else if (ib->alpha_info & FF_ALPHA_TRANSP)
|
|
alpha_mode = 'T';
|
|
else
|
|
alpha_mode = ' ';
|
|
|
|
buf_printf(out, "--%dx%d %s %c%c",
|
|
ib->width, ib->height,
|
|
avcodec_get_pix_fmt_name(ib->pix_fmt),
|
|
alpha_mode,
|
|
ib->interleaved ? 'I' : ' ');
|
|
}
|
|
|
|
static void pixel_format_complete(CompleteState *cp, CompleteFunc enumerate) {
|
|
int i;
|
|
const char *name;
|
|
|
|
for (i = 0; i < PIX_FMT_NB; i++) {
|
|
name = avcodec_get_pix_fmt_name(i);
|
|
(*enumerate)(cp, name, CT_IGLOB);
|
|
}
|
|
}
|
|
|
|
/* specific image commands */
|
|
static const CmdDef image_commands[] = {
|
|
CMD0( "image-rotate", "t", "",
|
|
image_rotate)
|
|
CMD2( "image-convert", "c", "",
|
|
image_convert, ESs,
|
|
"s{New pixel format: }[pixel-format]|pixel-format|")
|
|
CMD2( "image-set-background-color", "b", "",
|
|
image_set_background_color, ESs,
|
|
"s{Background color (use 'transparent' for tiling): }")
|
|
#if 1
|
|
CMD0( "image-normal-size", "n", "",
|
|
image_normal_size)
|
|
CMD1( "image-double-size", ">", "",
|
|
image_mult_size, 100)
|
|
CMD1( "image-halve-size", "<", "",
|
|
image_mult_size, -50)
|
|
CMD1( "image-larger-10", ".", "",
|
|
image_mult_size, 10)
|
|
CMD1( "image-smaller-10", ",", "",
|
|
image_mult_size, -10)
|
|
CMD2( "image-set-display-size", "S", "",
|
|
image_set_size, ESii,
|
|
"n{Displayed width: }"
|
|
"n{Displayed height: }")
|
|
#endif
|
|
};
|
|
|
|
static EditBufferDataType image_data_type = {
|
|
"image",
|
|
image_buffer_load,
|
|
image_buffer_save,
|
|
image_buffer_close,
|
|
};
|
|
|
|
/* additional mode specific bindings */
|
|
static const char * const image_bindings[] = {
|
|
"f", "toggle-full-screen",
|
|
NULL
|
|
};
|
|
|
|
static ModeDef image_mode = {
|
|
.name = "image",
|
|
.buffer_instance_size = sizeof(ImageBufferState),
|
|
.window_instance_size = sizeof(ImageState),
|
|
.mode_probe = image_mode_probe,
|
|
.mode_init = image_mode_init,
|
|
.mode_close = image_mode_close,
|
|
.mode_free = image_mode_free,
|
|
.display = image_display,
|
|
.move_up_down = image_move_up_down,
|
|
.move_left_right = image_move_left_right,
|
|
.scroll_up_down = image_scroll_up_down,
|
|
.data_type = &image_data_type,
|
|
.get_mode_line = image_mode_line,
|
|
.bindings = image_bindings,
|
|
};
|
|
|
|
static CompletionDef pixel_format_completion = {
|
|
"pixel-format", pixel_format_complete
|
|
};
|
|
|
|
static int image_init(QEmacsState *qs) {
|
|
av_register_all();
|
|
qe_register_data_type(qs, &image_data_type);
|
|
qe_register_mode(qs, &image_mode, MODEF_DATATYPE | MODEF_VIEW);
|
|
qe_register_commands(qs, &image_mode, image_commands, countof(image_commands));
|
|
qe_register_completion(qs, &pixel_format_completion);
|
|
return 0;
|
|
}
|
|
|
|
qe_module_init(image_init);
|