diff --git a/Lgi.xml b/Lgi.xml
--- a/Lgi.xml
+++ b/Lgi.xml
@@ -1,634 +1,635 @@
+
linux/Makefile.linux
win\Makefile.windows
mac/Makefile.mac
haiku/Makefile.haiku
gcc
0
./include
./private/common
./include
./private/common
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
/usr/include/libayatana-appindicator3-0.1
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
/usr/include/libayatana-appindicator3-0.1
./include/lgi/win
./private/win
./include/lgi/win
./private/win
./include/lgi/haiku
./private/haiku
./include/lgi/haiku
./private/haiku
libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
../../../../system/develop/headers
../../../../system/develop/headers
magic
appindicator3
crypt
gstreamer-1.0
`pkg-config --libs gtk+-3.0`
magic
appindicator3
crypt
gstreamer-1.0
`pkg-config --libs gtk+-3.0`
-static-libgcc
gnu
network
be
-static-libgcc
gnu
network
be
lgi-gtk3
lgi-gtk3
DynamicLibrary
LGI_LIBRARY
LGI_UNIT_TESTS
LGI_LIBRARY
LGI_UNIT_TESTS
POSIX
_GNU_SOURCE
POSIX
_GNU_SOURCE
-Wno-format-truncation
-Wno-format-truncation
diff --git a/include/lgi/common/Bitmap.h b/include/lgi/common/Bitmap.h
--- a/include/lgi/common/Bitmap.h
+++ b/include/lgi/common/Bitmap.h
@@ -1,28 +1,32 @@
/// \file
/// \author Matthew Allen (fret@memecode.com)
/// \brief Bitmap control
#pragma once
/// Bitmap control
class LgiClass LBitmap :
public LControl,
public ResObject
{
- LSurface *pDC;
+ LSurface *pDC = NULL;
+ bool ownDC = true;
class LThread *pThread;
public:
/// Construct the bitmap control with the usual parameters
LBitmap(int id, int x, int y, char *FileName, bool Async = false);
~LBitmap();
/// Sets the surface to display in the control
- virtual void SetDC(LSurface *pDC = 0);
+ virtual void SetDC(LSurface *pDC = NULL, bool owndc = true);
/// Gets the surface being displayed
virtual LSurface *GetSurface();
- LMessage::Result OnEvent(LMessage *Msg);
- void OnPaint(LSurface *pDC);
- void OnMouseClick(LMouse &m);
+ void Empty();
+
+ LMessage::Result OnEvent(LMessage *Msg) override;
+ void OnPaint(LSurface *pDC) override;
+ void OnMouseClick(LMouse &m) override;
+ bool OnLayout(LViewLayoutInfo &Inf) override;
};
diff --git a/src/common/Gdc2/Filters/Jpeg.cpp b/src/common/Gdc2/Filters/Jpeg.cpp
--- a/src/common/Gdc2/Filters/Jpeg.cpp
+++ b/src/common/Gdc2/Filters/Jpeg.cpp
@@ -1,1179 +1,1181 @@
/*hdr
** FILE: Jpeg.cpp
** AUTHOR: Matthew Allen
** DATE: 8/9/1998
** DESCRIPTION: Jpeg file filter
**
** Copyright (C) 1998, Matthew Allen
** fret@memecode.com
*/
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Palette.h"
#if HAS_LIBJPEG
#define XMD_H
#undef FAR
// If you don't want to build with JPEG support then set
// the define HAS_LIBJPEG to '0' in Lgi.h
//
// Linux:
// sudo apt-get install libjpeg-dev
#include "jpeglib.h"
#include
#include "lgi/common/LibraryUtils.h"
#if LIBJPEG_SHARED
#define JPEGLIB d->
const char sLibrary[] =
#if defined(LINUX)
"libjpeg"
#elif defined(HAIKU)
"libjpeg.so.62"
#else
"libjpeg9a"
#if defined(WINDOWS)
"_"
#if _MSC_VER >= _MSC_VER_VS2019
_MSC_YEAR_STR
#else
_MSC_VER_STR
#endif
#ifdef LGI_64BIT
"x64"
#else
"x32"
#endif
#ifdef _DEBUG
"d"
#endif
#endif
#endif
;
#else
#define JPEGLIB
#endif
// JPEG
#if LIBJPEG_SHARED
class LibJpeg : public LLibrary
{
public:
LibJpeg() : LLibrary(sLibrary)
{
static bool First = true;
if (First)
{
First = false;
if (IsLoaded())
{
LgiTrace("%s:%i - JPEG: %s\n", _FL, GetFullPath().Get());
}
else
{
#if defined(WINDOWS) && defined(_DEBUG)
auto ReleaseLib = LString(sLibrary)(0, -2);
if (!Load(ReleaseLib))
#endif
LgiTrace("%s:%i - JPEG: failed to find '%s'\n", _FL, sLibrary);
}
}
}
DynFunc1(boolean, jpeg_finish_decompress, j_decompress_ptr, cinfo);
DynFunc3(JDIMENSION, jpeg_read_scanlines, j_decompress_ptr, cinfo, JSAMPARRAY, scanlines, JDIMENSION, max_lines);
DynFunc1(boolean, jpeg_start_decompress, j_decompress_ptr, cinfo);
DynFunc2(int, jpeg_read_header, j_decompress_ptr, cinfo, boolean, require_image);
DynFunc2(int, jpeg_stdio_src, j_decompress_ptr, cinfo, FILE*, infile);
DynFunc1(int, jpeg_destroy_decompress, j_decompress_ptr, cinfo);
DynFunc1(int, jpeg_finish_compress, j_compress_ptr, cinfo);
DynFunc1(int, jpeg_destroy_compress, j_compress_ptr, cinfo);
DynFunc3(JDIMENSION, jpeg_write_scanlines, j_compress_ptr, cinfo, JSAMPARRAY, scanlines, JDIMENSION, num_lines);
DynFunc2(int, jpeg_start_compress, j_compress_ptr, cinfo, boolean, write_all_tables);
DynFunc1(int, jpeg_set_defaults, j_compress_ptr, cinfo);
DynFunc1(struct jpeg_error_mgr *, jpeg_std_error, struct jpeg_error_mgr *, err);
DynFunc2(int, jpeg_stdio_dest, j_compress_ptr, cinfo, FILE *, outfile);
DynFunc3(int, jpeg_CreateCompress, j_compress_ptr, cinfo, int, version, size_t, structsize);
DynFunc3(int, jpeg_CreateDecompress, j_decompress_ptr, cinfo, int, version, size_t, structsize);
DynFunc3(int, jpeg_set_quality, j_compress_ptr, cinfo, int, quality, boolean, force_baseline);
DynFunc3(int, jpeg_save_markers, j_decompress_ptr, cinfo, int, marker_code, unsigned int, length_limit);
};
LMutex JpegLibraryLock("JpegLibraryLock");
LAutoPtr JpegLibrary;
#endif
#include
#include
#include "lgi/common/LibraryUtils.h"
#include "lgi/common/ScrollBar.h"
#include "lgi/common/Variant.h"
#include "lgi/common/Jpeg.h"
#define IJG_JPEG_ICC_MARKER JPEG_APP0 + 2
#define IJG_JPEG_APP_ICC_PROFILE_LEN 0xFFFF
#define IJG_MAXIMUM_SEQUENCE_NUMBER 255
#define IJG_SEQUENCE_NUMBER_INDEX 12
#define IJG_NUMBER_OF_MARKERS_INDEX 13
#define IJG_JFIF_ICC_HEADER_LENGTH 14
/////////////////////////////////////////////////////////////////////
class GdcJpegFactory : public LFilterFactory
{
bool CheckFile(const char *File, int Access, const uchar *Hint)
{
if (Hint)
{
if (Hint[6] == 'J' &&
Hint[7] == 'F' &&
Hint[8] == 'I' &&
Hint[9] == 'F')
return true;
if (Hint[0] == 0xff &&
Hint[1] == 0xd8 &&
Hint[2] == 0xff &&
Hint[3] == 0xe1)
return true;
}
return (File) ? stristr(File, ".jpeg") != 0 ||
stristr(File, ".jpg") != 0 : false;
}
LFilter *NewObject()
{
#if LIBJPEG_SHARED
if (JpegLibraryLock.Lock(_FL))
{
if (!JpegLibrary)
JpegLibrary.Reset(new LibJpeg);
JpegLibraryLock.Unlock();
}
#endif
return new GdcJpeg;
}
} JpegFactory;
/////////////////////////////////////////////////////////////////////
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct my_error_mgr *my_error_ptr;
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
// (*cinfo->err->output_message)(cinfo);
char buf[256];
(*cinfo->err->format_message)(cinfo, buf);
longjmp(myerr->setjmp_buffer, 1);
}
bool IsJfifIccMarker(jpeg_saved_marker_ptr marker)
{
return marker->marker == IJG_JPEG_ICC_MARKER &&
marker->data_length >= IJG_JFIF_ICC_HEADER_LENGTH &&
GETJOCTET(marker->data[0]) == 0x49 &&
GETJOCTET(marker->data[1]) == 0x43 &&
GETJOCTET(marker->data[2]) == 0x43 &&
GETJOCTET(marker->data[3]) == 0x5F &&
GETJOCTET(marker->data[4]) == 0x50 &&
GETJOCTET(marker->data[5]) == 0x52 &&
GETJOCTET(marker->data[6]) == 0x4F &&
GETJOCTET(marker->data[7]) == 0x46 &&
GETJOCTET(marker->data[8]) == 0x49 &&
GETJOCTET(marker->data[9]) == 0x4C &&
GETJOCTET(marker->data[10]) == 0x45 &&
GETJOCTET(marker->data[11]) == 0x0;
}
bool GetIccProfile(j_decompress_ptr cinfo, uchar **icc_data, uint *icc_datalen)
{
int num_markers = 0;
jpeg_saved_marker_ptr markers = cinfo->marker_list;
JOCTET *local_icc_data = NULL;
int count = 0;
uint total_length;
bool marker_present[IJG_MAXIMUM_SEQUENCE_NUMBER];
uint data_length[IJG_MAXIMUM_SEQUENCE_NUMBER];
uint marker_sequence[IJG_MAXIMUM_SEQUENCE_NUMBER];
if (icc_data == NULL || icc_datalen == NULL)
return false;
*icc_data = NULL;
*icc_datalen = 0;
ZeroObj(marker_present);
for (markers = cinfo->marker_list; markers; markers = markers->next)
{
if (IsJfifIccMarker(markers))
{
int sequence_number = 0;
if (num_markers == 0)
{
num_markers = GETJOCTET(markers->data[IJG_NUMBER_OF_MARKERS_INDEX]);
}
else if (num_markers != GETJOCTET(markers->data[IJG_NUMBER_OF_MARKERS_INDEX]))
{
return false;
}
sequence_number = GETJOCTET(markers->data[IJG_SEQUENCE_NUMBER_INDEX]) - 1;
if (sequence_number < 0 || sequence_number > num_markers)
{
return false;
}
if (marker_present[sequence_number])
return false;
marker_present[sequence_number] = true;
data_length[sequence_number] =
markers->data_length - IJG_JFIF_ICC_HEADER_LENGTH;
marker_sequence[sequence_number] = count;
count++;
}
}
if (num_markers == 0)
return false;
total_length = 0;
markers = cinfo->marker_list;
for (count = 0; count < num_markers; count++)
{
int previous_length = total_length;
int index = 0;
int i = 0;
JOCTET *src_pointer;
JOCTET *dst_pointer;
index = marker_sequence[count];
if (!(marker_present[count]))
continue;
total_length += data_length[count];
if (local_icc_data != NULL)
break;
local_icc_data = (JOCTET *)malloc(total_length);
if (local_icc_data == NULL)
return false;
for (i = 0; i < index; i++)
{
markers = markers->next;
if (!markers)
{
free(local_icc_data);
return false;
}
}
dst_pointer = local_icc_data + previous_length;
src_pointer = markers->data + IJG_JFIF_ICC_HEADER_LENGTH;
while (data_length[count]--)
{
*dst_pointer++ = *src_pointer++;
}
}
*icc_data = local_icc_data;
*icc_datalen = total_length;
return true;
}
struct JpegStream
{
LStream *f;
LArray Buf;
};
boolean j_fill_input_buffer(j_decompress_ptr cinfo)
{
JpegStream *s = (JpegStream*)cinfo->client_data;
cinfo->src->next_input_byte = &s->Buf[0];
cinfo->src->bytes_in_buffer = s->f->Read((void*)cinfo->src->next_input_byte, s->Buf.Length());
return cinfo->src->bytes_in_buffer > 0;
}
void j_init_source(j_decompress_ptr cinfo)
{
JpegStream *s = (JpegStream*)cinfo->client_data;
s->Buf.Length(4 << 10);
j_fill_input_buffer(cinfo);
}
void j_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
{
// JpegStream *s = (JpegStream*)cinfo->client_data;
while (num_bytes)
{
long Remain = MIN((long)cinfo->src->bytes_in_buffer, num_bytes);
if (!Remain)
break;
cinfo->src->next_input_byte += Remain;
cinfo->src->bytes_in_buffer -= Remain;
num_bytes -= Remain;
if (num_bytes)
{
j_fill_input_buffer(cinfo);
}
}
}
boolean j_resync_to_restart(j_decompress_ptr cinfo, int desired)
{
// JpegStream *s = (JpegStream*)cinfo->client_data;
LAssert(0); // not impl
return false;
}
void j_term_source(j_decompress_ptr cinfo)
{
JpegStream *s = (JpegStream*)cinfo->client_data;
s->Buf.Length(0);
}
GdcJpeg::GdcJpeg()
{
#if LIBJPEG_SHARED
d = JpegLibrary;
#endif
}
typedef LRgb24 JpegRgb;
typedef LCmyk32 JpegCmyk;
struct JpegXyz
{
uint8_t x, y, z;
};
template
void Convert16(T *d, int width)
{
JpegRgb *s = (JpegRgb*) d;
JpegRgb *e = s + width;
while (s < e)
{
JpegRgb t = *s++;
d->r = t.r >> 3;
d->g = t.g >> 2;
d->b = t.b >> 3;
d++;
}
}
template
void Convert24(T *d, int width)
{
JpegRgb *e = (JpegRgb*) d;
JpegRgb *s = e;
d += width - 1;
s += width - 1;
while (s >= e)
{
JpegRgb t = *s--;
d->r = t.r;
d->g = t.g;
d->b = t.b;
d--;
}
}
template
void Convert32(T *d, int width)
{
JpegRgb *e = (JpegRgb*) d;
JpegRgb *s = e;
d += width - 1;
s += width - 1;
while (s >= e)
{
JpegRgb t = *s--;
d->r = t.r;
d->g = t.g;
d->b = t.b;
d->a = 255;
d--;
}
}
#define Rc 0
#define Gc (MaxRGB+1)
#define Bc (MaxRGB+1)*2
#define DownShift(x) ((int) ((x)+(1L << 15)) >> 16)
#define UpShifted(x) ((int) ((x)*(1L << 16)+0.5))
#define MaxRGB 255
template
void Ycc24( T *d,
int width,
long *red,
long *green,
long *blue,
uchar *range_table)
{
JpegXyz *s = (JpegXyz*) d;
T *e = d;
uchar *range_limit = range_table + (MaxRGB + 1);
d += width - 1;
s += width - 1;
while (d >= e)
{
/* Y = 0.299 * R + 0.587 * G + 0.114 * B
Cb = -0.169 * R - 0.331 * G + 0.500 * B
Cr = 0.500 * R - 0.419 * G - 0.081 * B */
int x = s->x;
int y = s->y;
int z = s->z;
d->r = range_limit[DownShift(red[x+Rc] + green[y+Rc] + blue[z+Rc])];
d->g = range_limit[DownShift(red[x+Gc] + green[y+Gc] + blue[z+Gc])];
d->b = range_limit[DownShift(red[x+Bc] + green[y+Bc] + blue[z+Bc])];
d--;
s--;
}
}
template
void Ycc32( T *d,
int width,
long *red,
long *green,
long *blue,
uchar *range_table)
{
JpegXyz *s = (JpegXyz*) d;
T *e = d;
uchar *range_limit = range_table + (MaxRGB + 1);
d += width - 1;
s += width - 1;
while (d >= e)
{
/* Y = 0.299 * R + 0.587 * G + 0.114 * B
Cb = -0.169 * R - 0.331 * G + 0.500 * B
Cr = 0.500 * R - 0.419 * G - 0.081 * B */
int x = s->x;
int y = s->y;
int z = s->z;
d->r = range_limit[DownShift(red[x+Rc] + green[y+Rc] + blue[z+Rc])];
d->g = range_limit[DownShift(red[x+Gc] + green[y+Gc] + blue[z+Gc])];
d->b = range_limit[DownShift(red[x+Bc] + green[y+Bc] + blue[z+Bc])];
d->a = 255;
d--;
s--;
}
}
template
void CmykToRgb24(D *d, S *s, int width)
{
D *end = d + width;
while (d < end)
{
int k = s->k;
d->r = s->c * k / 255;
d->g = s->m * k / 255;
d->b = s->y * k / 255;
d++;
s++;
}
}
template
void CmykToRgb32(D *d, S *s, int width)
{
D *end = d + width;
while (d < end)
{
int k = s->k;
int c = s->c * k;
int m = s->m * k;
int y = s->y * k;
d->r = c / 255;
d->g = m / 255;
d->b = y / 255;
d->a = 255;
d++;
s++;
}
}
LFilter::IoStatus GdcJpeg::ReadImage(LSurface *pDC, LStream *In)
{
LFilter::IoStatus Status = IoError;
LVariant v;
#if LIBJPEG_SHARED
+ if (!d)
+ return IoError;
if (!d->IsLoaded() &&
!d->Load(sLibrary)
#ifdef WIN32
&& !d->Load(LString(sLibrary).Strip("d"))
#endif
)
{
if (Props)
{
LString s;
s.Printf("libjpeg is missing (%s.%s)", sLibrary, LGI_LIBRARY_EXT);
Props->SetValue(LGI_FILTER_ERROR, v = s);
}
static bool Warn = true;
if (Warn)
{
LgiTrace("%s:%i - Unable to load libjpg (%s.%s).\n", _FL, sLibrary, LGI_LIBRARY_EXT);
Warn = false;
}
return LFilter::IoComponentMissing;
}
#endif
if (!pDC || !In)
{
return Status;
}
int row_stride;
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
JpegStream s;
s.f = In;
ZeroObj(cinfo);
cinfo.err = JPEGLIB jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
cinfo.client_data = &s;
if (setjmp(jerr.setjmp_buffer))
{
char msg[JMSG_LENGTH_MAX] = "format_message failed";
(*cinfo.err->format_message)((j_common_ptr)&cinfo, msg);
// const char *msg = e->jpeg_message_table[cinfo.err->msg_code];
if (Props)
{
Props->SetValue(LGI_FILTER_ERROR, v = msg);
}
JPEGLIB jpeg_destroy_decompress(&cinfo);
return Status;
}
JPEGLIB jpeg_create_decompress(&cinfo);
jpeg_source_mgr Source;
cinfo.mem->max_memory_to_use = 512 << 20;
ZeroObj(Source);
cinfo.src = &Source;
cinfo.src->init_source = j_init_source;
cinfo.src->fill_input_buffer = j_fill_input_buffer;
cinfo.src->skip_input_data = j_skip_input_data;
cinfo.src->resync_to_restart = j_resync_to_restart;
cinfo.src->term_source = j_term_source;
JPEGLIB jpeg_save_markers
(
&cinfo,
IJG_JPEG_ICC_MARKER,
IJG_JPEG_APP_ICC_PROFILE_LEN
);
int result = JPEGLIB jpeg_read_header(&cinfo, true);
if (result != JPEG_HEADER_OK)
{
return IoError;
}
uchar *icc_data = 0;
uint icc_len = 0;
if (Props && GetIccProfile(&cinfo, &icc_data, &icc_len))
{
v.SetBinary(icc_len, icc_data);
Props->SetValue(LGI_FILTER_COLOUR_PROF, v);
free(icc_data);
icc_data = 0;
}
if (Props)
{
LStringPipe s(256);
s.Print("Sub-sampling: ");
for (int i=0; iSetValue(LGI_FILTER_INFO, v = ss.Get());
}
int Bits = cinfo.num_components * 8;
if (pDC->Create(cinfo.image_width, cinfo.image_height, LBitsToColourSpace(Bits)))
{
// zero out bitmap
pDC->Colour(0, Bits);
pDC->Rectangle();
// Progress
if (Meter)
{
Meter->SetDescription("scanlines");
Meter->SetRange(cinfo.image_height);
}
// Read
if (cinfo.num_components == 1)
{
// greyscale
LPalette *Pal = new LPalette(0, 256);
if (Pal)
{
auto p = (*Pal)[0];
if (p)
{
for (int i=0; i<256; i++, p++)
{
p->r = i;
p->g = i;
p->b = i;
}
}
pDC->Palette(Pal);
}
}
// setup decompress
JPEGLIB jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
long *red = new long[3*(MaxRGB+1)];
long *green = new long[3*(MaxRGB+1)];
long *blue = new long[3*(MaxRGB+1)];
uchar *range_table = new uchar[3*(MaxRGB+1)];
if (red &&
green &&
blue &&
range_table)
{
int i;
for (i=0; i<=MaxRGB; i++)
{
range_table[i]=0;
range_table[i+(MaxRGB+1)]=(uchar) i;
range_table[i+(MaxRGB+1)*2]=MaxRGB;
}
// uchar *range_limit=range_table+(MaxRGB+1);
for (i=0; i<=MaxRGB; i++)
{
red[i+Rc] = UpShifted(1.00000)*i;
green[i+Rc] = 0;
blue[i+Rc] = UpShifted(1.40200*0.5) * ((i << 1)-MaxRGB);
red[i+Gc] = UpShifted(1.00000)*i;
green[i+Gc] = (-UpShifted(0.34414*0.5)) * ((i << 1)-MaxRGB);
blue[i+Gc] = (-UpShifted(0.71414*0.5)) * ((i << 1)-MaxRGB);
red[i+Bc] = UpShifted(1.00000)*i;
green[i+Bc] = UpShifted(1.77200*0.5) * ((i << 1)-MaxRGB);
blue[i+Bc] = 0;
}
// loop through scanlines
Status = IoSuccess;
while ( cinfo.output_scanline < cinfo.output_height &&
Status == IoSuccess)
{
uchar *Ptr = (*pDC)[cinfo.output_scanline];
if (Ptr)
{
if (JPEGLIB jpeg_read_scanlines(&cinfo, &Ptr, 1))
{
switch ((int) cinfo.jpeg_color_space)
{
default:
break;
case JCS_GRAYSCALE:
{
// do nothing
break;
}
case JCS_CMYK:
{
if (cinfo.num_components != 4)
{
LAssert(!"Weird number of components for CMYK JPEG.");
break;
}
LAssert(pDC->GetBits() == 32);
switch (pDC->GetColourSpace())
{
#define CmykCase(name, bits) \
case Cs##name: CmykToRgb##bits((L##name*)Ptr, (LCmyk32*)Ptr, pDC->X()); break
CmykCase(Rgb24, 24);
CmykCase(Bgr24, 24);
CmykCase(Rgbx32, 24);
CmykCase(Bgrx32, 24);
CmykCase(Xrgb32, 24);
CmykCase(Xbgr32, 24);
CmykCase(Rgba32, 32);
CmykCase(Bgra32, 32);
CmykCase(Argb32, 32);
CmykCase(Abgr32, 32);
#undef CmykCase
default:
LAssert(!"impl me.");
Status = IoUnsupportedFormat;
break;
}
break;
}
case JCS_YCCK: // YCbCrK
case 10000: // YCbCr
{
if (cinfo.num_components == 3)
{
switch (pDC->GetColourSpace())
{
#define YccCase(name, bits) \
case Cs##name: Ycc##bits((L##name*)Ptr, pDC->X(), red, green, blue, range_table); break;
YccCase(Rgb24, 24);
YccCase(Bgr24, 24);
YccCase(Xrgb32, 24);
YccCase(Xbgr32, 24);
YccCase(Rgbx32, 24);
YccCase(Bgrx32, 24);
YccCase(Argb32, 32);
YccCase(Abgr32, 32);
YccCase(Rgba32, 32);
YccCase(Bgra32, 32);
default:
LAssert(!"Unsupported colour space.");
Status = IoUnsupportedFormat;
break;
}
}
break;
}
case JCS_RGB:
case JCS_YCbCr:
{
if (cinfo.num_components == 3)
{
int Width = pDC->X();
switch (pDC->GetColourSpace())
{
#define JpegCase(name, bits) \
case Cs##name: Convert##bits((L##name*)Ptr, Width); break;
JpegCase(Rgb16, 16);
JpegCase(Bgr16, 16);
JpegCase(Rgb24, 24);
JpegCase(Bgr24, 24);
JpegCase(Xrgb32, 24);
JpegCase(Xbgr32, 24);
JpegCase(Rgbx32, 24);
JpegCase(Bgrx32, 24);
JpegCase(Argb32, 32);
JpegCase(Abgr32, 32);
JpegCase(Rgba32, 32);
JpegCase(Bgra32, 32);
default:
LAssert(!"Unsupported colour space.");
Status = IoUnsupportedFormat;
break;
}
}
break;
}
}
}
else break;
}
if (Meter)
{
Meter->Value(cinfo.output_scanline);
if (Meter->IsCancelled())
Status = IoCancel;
}
}
}
DeleteArray(red);
DeleteArray(green);
DeleteArray(blue);
DeleteArray(range_table);
JPEGLIB jpeg_finish_decompress(&cinfo);
}
JPEGLIB jpeg_destroy_decompress(&cinfo);
return Status;
}
void j_init_destination(j_compress_ptr cinfo)
{
JpegStream *Stream = (JpegStream*)cinfo->client_data;
Stream->Buf.Length(4 << 10);
cinfo->dest->free_in_buffer = Stream->Buf.Length();
cinfo->dest->next_output_byte = &Stream->Buf[0];
}
boolean j_empty_output_buffer(j_compress_ptr cinfo)
{
JpegStream *Stream = (JpegStream*)cinfo->client_data;
Stream->f->Write(&Stream->Buf[0], Stream->Buf.Length());
cinfo->dest->next_output_byte = &Stream->Buf[0];
cinfo->dest->free_in_buffer = Stream->Buf.Length();
return true;
}
void j_term_destination(j_compress_ptr cinfo)
{
JpegStream *Stream = (JpegStream*)cinfo->client_data;
auto Bytes = Stream->Buf.Length() - cinfo->dest->free_in_buffer;
if (Stream->f->Write(&Stream->Buf[0], Bytes) != Bytes)
LAssert(!"Write failed.");
}
LFilter::IoStatus GdcJpeg::WriteImage(LStream *Out, LSurface *pDC)
{
LVariant v;
#if LIBJPEG_SHARED
if (!d->IsLoaded() && !d->Load(sLibrary))
{
if (Props)
Props->SetValue(LGI_FILTER_ERROR, v = "libjpeg library isn't installed or wasn't usable.");
static bool Warn = true;
if (Warn)
{
LgiTrace("%s:%i - Unabled to load libjpeg.\n", _FL);
Warn = false;
}
return LFilter::IoComponentMissing;
}
#endif
// bool Ok = true;
// Setup quality setting
LVariant Quality(80), SubSample(Sample_1x1_1x1_1x1), DpiX, DpiY;
LPoint Dpi;
if (Props)
{
Props->GetValue(LGI_FILTER_QUALITY, Quality);
Props->GetValue(LGI_FILTER_SUBSAMPLE, SubSample);
Props->GetValue(LGI_FILTER_DPI_X, DpiX);
Props->GetValue(LGI_FILTER_DPI_Y, DpiY);
Dpi.x = DpiX.CastInt32();
Dpi.y = DpiY.CastInt32();
}
if (!Dpi.x)
Dpi.x = 300;
if (!Dpi.y)
Dpi.y = 300;
return _Write(Out, pDC, Quality.CastInt32(), (SubSampleMode)SubSample.CastInt32(), Dpi);
}
template
void Rop24(LRgb24 *dst, I *p, int x)
{
I *end = p + x;
while (p < end)
{
dst->r = p->r;
dst->g = p->g;
dst->b = p->b;
dst++;
p++;
}
}
LFilter::IoStatus GdcJpeg::_Write(LStream *Out, LSurface *pDC, int Quality, SubSampleMode SubSample, LPoint Dpi)
{
struct jpeg_compress_struct cinfo;
struct my_error_mgr jerr;
int row_stride;
cinfo.err = JPEGLIB jpeg_std_error(&jerr.pub);
JPEGLIB jpeg_create_compress(&cinfo);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer))
{
JPEGLIB jpeg_destroy_compress(&cinfo);
return LFilter::IoError;
}
JpegStream Stream;
Stream.f = Out;
cinfo.client_data = &Stream;
jpeg_destination_mgr Dest;
ZeroObj(Dest);
Dest.init_destination = j_init_destination;
Dest.empty_output_buffer = j_empty_output_buffer;
Dest.term_destination = j_term_destination;
cinfo.dest = &Dest;
bool GreyScale = FALSE;
LPalette *Pal = pDC->Palette();
if (pDC->GetBits() == 8)
{
if (Pal)
{
GreyScale = true;
for (int i=0; iGetSize(); i++)
{
auto p = (*Pal)[i];
if
(
p &&
(
p->r != i ||
p->g != i ||
p->b != i
)
)
{
GreyScale = FALSE;
break;
}
}
}
else if (!Pal)
{
GreyScale = true;
}
}
cinfo.image_width = pDC->X();
cinfo.image_height = pDC->Y();
if (GreyScale)
{
cinfo.input_components = 1;
cinfo.in_color_space = JCS_GRAYSCALE;
}
else
{
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
}
JPEGLIB jpeg_set_defaults(&cinfo);
switch (SubSample)
{
default:
case Sample_1x1_1x1_1x1:
cinfo.comp_info[0].h_samp_factor = 1;
cinfo.comp_info[0].v_samp_factor = 1;
break;
case Sample_2x2_1x1_1x1:
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 2;
break;
case Sample_2x1_1x1_1x1:
cinfo.comp_info[0].h_samp_factor = 2;
cinfo.comp_info[0].v_samp_factor = 1;
break;
case Sample_1x2_1x1_1x1:
cinfo.comp_info[0].h_samp_factor = 1;
cinfo.comp_info[0].v_samp_factor = 2;
break;
}
cinfo.comp_info[1].h_samp_factor = 1;
cinfo.comp_info[1].v_samp_factor = 1;
cinfo.comp_info[2].h_samp_factor = 1;
cinfo.comp_info[2].v_samp_factor = 1;
if (Quality >= 0)
{
JPEGLIB jpeg_set_quality(&cinfo, Quality, true);
}
cinfo.X_density = Dpi.x;
cinfo.Y_density = Dpi.y;
cinfo.density_unit = Dpi.x && Dpi.y ? 1 : 0;
JPEGLIB jpeg_start_compress(&cinfo, true);
auto Status = IoSuccess;
row_stride = pDC->X() * cinfo.input_components;
uchar *Buffer = new uchar[row_stride];
if (Buffer)
{
// Progress
if (Meter)
{
Meter->SetDescription("scanlines");
Meter->SetRange(cinfo.image_height);
}
// Write
while (cinfo.next_scanline < cinfo.image_height)
{
uchar *dst = Buffer;
switch (pDC->GetColourSpace())
{
case CsIndex8:
{
if (GreyScale)
{
memcpy(Buffer, (*pDC)[cinfo.next_scanline], pDC->X());
}
else if (Pal)
{
auto p = (*Pal)[0];
if (p)
{
uint8_t *c = (*pDC)[cinfo.next_scanline];
uint8_t *end = c + pDC->X();
while (c < end)
{
auto &rgb = p[*c++];
dst[0] = rgb.r;
dst[1] = rgb.g;
dst[2] = rgb.b;
dst += 3;
}
}
}
break;
}
case CsRgb16:
{
LRgb16 *p = (LRgb16*)(*pDC)[cinfo.next_scanline];
LRgb16 *end = p + pDC->X();
while (p < end)
{
dst[0] = G5bitTo8bit(p->r);
dst[1] = G6bitTo8bit(p->g);
dst[2] = G5bitTo8bit(p->b);
dst += 3;
p++;
}
break;
}
case CsBgr16:
{
LBgr16 *p = (LBgr16*)(*pDC)[cinfo.next_scanline];
LBgr16 *end = p + pDC->X();
while (p < end)
{
dst[0] = (p->r << 3) | (p->r >> 5);
dst[1] = (p->g << 2) | (p->g >> 6);
dst[2] = (p->b << 3) | (p->b >> 5);
dst += 3;
p++;
}
break;
}
#define Write24(cs) \
case Cs##cs: \
Rop24((LRgb24*)dst, (L##cs*)(*pDC)[cinfo.next_scanline], pDC->X()); \
break
Write24(Rgb24);
Write24(Bgr24);
Write24(Rgbx32);
Write24(Bgrx32);
Write24(Xrgb32);
Write24(Xbgr32);
case System32BitColourSpace:
{
System32BitPixel *p = (System32BitPixel*) (*pDC)[cinfo.next_scanline];
System32BitPixel *end = p + pDC->X();
while (p < end)
{
*dst++ = p->r;
*dst++ = p->g;
*dst++ = p->b;
p++;
}
break;
}
default:
LAssert(0);
break;
}
auto old = cinfo.next_scanline;
auto wr = JPEGLIB jpeg_write_scanlines(&cinfo, &Buffer, 1);
if (old == cinfo.next_scanline)
{
LgiTrace("%s:%i - jpeg_write_scanlines didn't advance next_scanline? wr=%i\n", _FL, (int)wr);
Status = IoError;
break;
}
if (Meter) Meter->Value(cinfo.next_scanline);
}
DeleteArray(Buffer);
}
JPEGLIB jpeg_finish_compress(&cinfo);
JPEGLIB jpeg_destroy_compress(&cinfo);
return Status;
}
#endif
diff --git a/src/common/Widgets/Bitmap.cpp b/src/common/Widgets/Bitmap.cpp
--- a/src/common/Widgets/Bitmap.cpp
+++ b/src/common/Widgets/Bitmap.cpp
@@ -1,195 +1,248 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Bitmap.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/LgiRes.h"
//////////////////////////////////////////////////////////////////////////////////
#ifdef _MT
#include "lgi/common/Thread.h"
class LBitmapThread : public LThread
{
LBitmap *Bmp;
char *File;
LThread **Owner;
public:
LBitmapThread(LBitmap *bmp, char *file, LThread **owner) : LThread("LBitmapThread")
{
Bmp = bmp;
File = NewStr(file);
Owner = owner;
if (Owner)
{
*Owner = this;
}
Run();
}
~LBitmapThread()
{
if (Owner)
{
*Owner = 0;
}
DeleteArray(File);
}
int Main()
{
if (Bmp)
{
LSurface *pDC = GdcD->Load(File);
if (pDC)
{
Bmp->SetDC(pDC);
#ifndef __GTK_H__
while (!Bmp->Handle())
LSleep(10);
#endif
LRect r = Bmp->GetPos();
r.SetSize(pDC->X()+4, pDC->Y()+4);
Bmp->SetPos(r, true);
Bmp->SendNotify();
DeleteObj(pDC);
}
}
return 0;
}
};
#endif
LBitmap::LBitmap(int id, int x, int y, char *FileName, bool Async)
: ResObject(Res_Bitmap)
{
pDC = 0;
pThread = 0;
#if WINNATIVE
SetStyle(WS_CHILD | WS_VISIBLE);
#endif
SetId(id);
LRect r(0, 0, 4, 4);
r.Offset(x, y);
if (ValidStr(FileName))
{
#ifdef _MT
if (!Async)
{
#endif
pDC = GdcD->Load(FileName);
if (pDC)
{
r.SetSize(pDC->X()+4, pDC->Y()+4);
}
#ifdef _MT
}
else
{
new LBitmapThread(this, FileName, &pThread);
}
#endif
}
SetPos(r);
LResources::StyleElement(this);
}
LBitmap::~LBitmap()
{
#ifdef _MT
if (pThread)
{
pThread->Terminate();
DeleteObj(pThread);
}
#endif
- DeleteObj(pDC);
+ Empty();
+}
+
+void LBitmap::Empty()
+{
+ if (ownDC)
+ {
+ DeleteObj(pDC);
+ }
+ else pDC = NULL;
}
-void LBitmap::SetDC(LSurface *pNewDC)
+bool LBitmap::OnLayout(LViewLayoutInfo &Inf)
{
- DeleteObj(pDC);
+ if (Inf.Width.Min)
+ {
+ // Height
+ if (pDC)
+ {
+ Inf.Height.Min = pDC->Y();
+ Inf.Height.Max = pDC->Y();
+ }
+ else
+ {
+ Inf.Height.Min = 32;
+ Inf.Height.Max = 32;
+ }
+ }
+ else
+ {
+ // Width
+ if (pDC)
+ {
+ Inf.Width.Min = pDC->X();
+ Inf.Width.Max = pDC->X();
+ }
+ else
+ {
+ Inf.Width.Min = 32;
+ Inf.Width.Max = 32;
+ }
+ }
+
+ return true;
+}
+
+void LBitmap::SetDC(LSurface *pNewDC, bool owndc)
+{
+ Empty();
+ ownDC = owndc;
if (pNewDC)
{
- pDC = new LMemDC;
- if (pDC && pDC->Create(pNewDC->X(), pNewDC->Y(), GdcD->GetColourSpace()))
- {
- LColour Bk = LColour(L_WORKSPACE);
- if (GetCss())
+ if (ownDC)
+ {
+ pDC = new LMemDC;
+ if (pDC && pDC->Create(pNewDC->X(), pNewDC->Y(), GdcD->GetColourSpace()))
{
- LCss::ColorDef b = GetCss()->BackgroundColor();
- if (b.IsValid())
- Bk = b;
- }
+ LColour Bk = LColour(L_WORKSPACE);
+ if (GetCss())
+ {
+ LCss::ColorDef b = GetCss()->BackgroundColor();
+ if (b.IsValid())
+ Bk = b;
+ }
- pDC->Colour(Bk);
- pDC->Rectangle();
+ pDC->Colour(Bk);
+ pDC->Rectangle();
- pDC->Op(GDC_ALPHA);
- pDC->Blt(0, 0, pNewDC);
+ pDC->Op(GDC_ALPHA);
+ pDC->Blt(0, 0, pNewDC);
- LRect r = GetPos();
- r.SetSize(pDC->X() + 4, pDC->Y() + 4);
- SetPos(r);
- Invalidate();
+ LRect r = GetPos();
+ r.SetSize(pDC->X() + 4, pDC->Y() + 4);
+ SetPos(r);
+ Invalidate();
- for (LViewI *p = GetParent(); p; p = p->GetParent())
- {
- LTableLayout *Tl = dynamic_cast(p);
- if (Tl)
+ for (LViewI *p = GetParent(); p; p = p->GetParent())
{
- Tl->InvalidateLayout();
- break;
+ LTableLayout *Tl = dynamic_cast(p);
+ if (Tl)
+ {
+ Tl->InvalidateLayout();
+ break;
+ }
}
+
}
-
- SendNotify(LNotifyTableLayoutRefresh);
+ }
+ else
+ {
+ pDC = pNewDC;
}
+
+ SendNotify(LNotifyTableLayoutRefresh);
}
+ Invalidate();
}
LSurface *LBitmap::GetSurface()
{
return pDC;
}
LMessage::Result LBitmap::OnEvent(LMessage *Msg)
{
return LView::OnEvent(Msg);
}
void LBitmap::OnPaint(LSurface *pScreen)
{
LColour cBack = StyleColour(LCss::PropBackgroundColor, LColour(L_MED));
LRect a = GetClient();
pScreen->Colour(cBack);
pScreen->Rectangle(&a);
if (pDC)
{
pScreen->Op(GDC_ALPHA);
pScreen->Blt(0, 0, pDC);
}
}
void LBitmap::OnMouseClick(LMouse &m)
{
if (!m.Down() && GetParent())
{
LDialog *Dlg = dynamic_cast(GetParent());
if (Dlg)
{
- LNotification note(m);
+ LNotification note(m);
Dlg->OnNotify(this, note);
}
}
}