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); } } }