summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/display/cairo-utils.cpp86
-rw-r--r--src/display/cairo-utils.h2
-rw-r--r--src/helper/png-write.cpp77
-rw-r--r--src/helper/png-write.h6
-rw-r--r--src/ui/dialog/export.cpp61
-rw-r--r--src/ui/dialog/export.h14
6 files changed, 208 insertions, 38 deletions
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index 24a75de4c..a7625f4a1 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -13,6 +13,7 @@
#endif
#include "display/cairo-utils.h"
+#include <arpa/inet.h>
#include <stdexcept>
#include <glib/gstdio.h>
@@ -1321,6 +1322,91 @@ guint32 argb32_from_rgba(guint32 in)
return px;
}
+
+/**
+ * Converts a pixbuf to a PNG data structure.
+ * For 8-but RGBA png, this is like copying.
+ *
+ */
+const guchar* pixbuf_to_png(guchar const**rows, guchar* px, int num_rows, int num_cols, int stride, int color_type, int bit_depth)
+{
+ int n_fields = 1 + (color_type&2) + (color_type&4)/4;
+ const guchar* new_data = (const guchar*)malloc((n_fields * bit_depth * num_rows * num_cols)/8 + 64);
+ char* ptr = (char*) new_data;
+ int pad=0; //used when we write image data smaller than one byte (for instance in black and white images where 1px = 1bit)
+ for(int row = 0; row < num_rows; ++row){
+ rows[row] = (const guchar*)ptr;
+ for(int col = 0; col < num_cols; ++col){
+ guint32 *pixel = reinterpret_cast<guint32*>(px + row*stride)+col;
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ //this part should probably be rewritten as (a tested, working) big endian, using htons() and ntohs()
+ guint64 a = (*pixel & 0xff000000) >> 24;
+ guint64 b = (*pixel & 0x00ff0000) >> 16;
+ guint64 g = (*pixel & 0x0000ff00) >> 8;
+ guint64 r = (*pixel & 0x000000ff);
+
+ //one of possible rgb to greyscale formulas. This one is called "luminance, "luminosity" or "luma"
+ guint16 gray = (guint16)((guint32)((0.2126*(r<<24) + 0.7152*(g<<24) + 0.0722*(b<<24)))>>16);
+
+ if (!pad) *((guint64*)ptr)=0;
+ if (color_type & 2) { //RGB
+ // for 8bit->16bit transition, I take the FF -> FFFF convention (multiplication by 0x101).
+ // If you prefer FF -> FF00 (multiplication by 0x100), remove the <<8, <<24, <<40 and <<56
+ if (color_type & 4) { //RGBA
+ if (bit_depth==8)
+ *((guint32*)ptr) = *pixel;
+ else
+ *((guint64*)ptr) = (guint64)((a<<56)+(a<<48)+(b<<40)+(b<<32)+(g<<24)+(g<<16)+(r<<8)+(r));
+ }
+ else{ //no alpha
+ if(bit_depth==8)
+ *((guint32*)ptr) = ((*pixel)<<8)>>8;
+ else
+ *((guint64*)ptr) = (guint64)((b<<40)+(b<<32)+(g<<24)+(g<<16)+(r<<8)+r);
+ }
+ } else { //Grayscale
+ if(bit_depth==16)
+ *(guint16*)ptr= ((gray & 0xff00)>>8) + ((gray &0x00ff)<<8);
+ else *((guint16*)ptr) += guint16(((gray >> (16-bit_depth))<<pad) ); //note the "+="
+
+ if(color_type & 4) { //Alpha channel
+ if (bit_depth == 16)
+ *((guint32*)(ptr+2)) = a + (a<<8);
+ else
+ *((guint32*)(ptr)) += guint32((a << 8) >> (16 - bit_depth))<<(bit_depth + pad);
+ }
+ }
+
+#else
+ // I don't have any access to a big-endian machine to test this. It should still work with default export settings.
+ guint64 r = (*pixel & 0xff000000) >> 24;
+ guint64 g = (*pixel & 0x00ff0000) >> 16;
+ guint64 b = (*pixel & 0x0000ff00) >> 8;
+ guint64 a = (*pixel & 0x000000ff);
+ guint32 gray = (guint32)(0.2126*(r<<24) + 0.7152*(g<<24) + 0.0722*(b<<24));
+ if(color_type & 2){
+ if(bit_depth==8)*ptr = *pixel; else *ptr = (guint64)((r<<56)+(g<<40)+(b<<24)+(a<<8));
+ } else {
+ *((guint32*)ptr) += guint32(gray>>pad);
+ if(color_type & 4) *((guint32*)ptr) += guint32((a>>pad)>> bit_depth);
+ }
+#endif
+ pad+=bit_depth*n_fields;
+ ptr+=(pad/8);
+ pad%=8;
+ }
+ if(pad){pad=0;ptr++;}//align bytes on rows
+ }
+ return new_data;
+}
+
+
+
+
+
+
+
+
/*
Local Variables:
mode:c++
diff --git a/src/display/cairo-utils.h b/src/display/cairo-utils.h
index 2a7e460e8..72087471d 100644
--- a/src/display/cairo-utils.h
+++ b/src/display/cairo-utils.h
@@ -178,6 +178,8 @@ void convert_pixels_argb32_to_pixbuf(guchar *data, int w, int h, int rs);
G_GNUC_CONST guint32 argb32_from_pixbuf(guint32 in);
G_GNUC_CONST guint32 pixbuf_from_argb32(guint32 in);
+const guchar* pixbuf_to_png(guchar const**rows, guchar* px, int nrows, int ncols, int stride, int color_type, int bit_depth);
+
/** Convert a pixel in 0xRRGGBBAA format to Cairo ARGB32 format. */
G_GNUC_CONST guint32 argb32_from_rgba(guint32 in);
diff --git a/src/helper/png-write.cpp b/src/helper/png-write.cpp
index 682aee9b2..f270dcc95 100644
--- a/src/helper/png-write.cpp
+++ b/src/helper/png-write.cpp
@@ -123,8 +123,8 @@ void PngTextList::add(gchar const* key, gchar const* text)
static bool
sp_png_write_rgba_striped(SPDocument *doc,
gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi,
- int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data),
- void *data)
+ int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth),
+ void *data, bool interlace, int color_type, int bit_depth, int zlib)
{
g_return_val_if_fail(filename != NULL, false);
g_return_val_if_fail(data != NULL, false);
@@ -184,22 +184,28 @@ sp_png_write_rgba_striped(SPDocument *doc,
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
*/
+
+ png_set_compression_level(png_ptr, zlib);
+
png_set_IHDR(png_ptr, info_ptr,
width,
height,
- 8, /* bit_depth */
- PNG_COLOR_TYPE_RGB_ALPHA,
- PNG_INTERLACE_NONE,
+ bit_depth,
+ color_type,
+ interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
- /* otherwise, if we are dealing with a color image then */
- sig_bit.red = 8;
- sig_bit.green = 8;
- sig_bit.blue = 8;
- /* if the image has an alpha channel then */
- sig_bit.alpha = 8;
- png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+ if ((color_type&2) && bit_depth == 16) {
+ // otherwise, if we are dealing with a color image then
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ // if the image has an alpha channel then
+ if (color_type&4)
+ sig_bit.alpha = 8;
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+ }
PngTextList textList;
@@ -248,7 +254,10 @@ sp_png_write_rgba_striped(SPDocument *doc,
/* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */
/* note that if sRGB is present the cHRM chunk must be ignored
* on read and must be written in accordance with the sRGB profile */
- png_set_pHYs(png_ptr, info_ptr, unsigned(xdpi / 0.0254 + 0.5), unsigned(ydpi / 0.0254 + 0.5), PNG_RESOLUTION_METER);
+ if(xdpi < 0.0254 ) xdpi = 0.0255;
+ if(ydpi < 0.0254 ) ydpi = 0.0255;
+
+ png_set_pHYs(png_ptr, info_ptr, unsigned(xdpi / 0.0254 ), unsigned(ydpi / 0.0254 ), PNG_RESOLUTION_METER);
/* Write the file header information. REQUIRED */
png_write_info(png_ptr, info_ptr);
@@ -271,15 +280,18 @@ sp_png_write_rgba_striped(SPDocument *doc,
*/
png_bytep* row_pointers = new png_bytep[ebp->sheight];
-
- r = 0;
- while (r < static_cast<png_uint_32>(height)) {
- void *to_free;
- int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data);
- if (!n) break;
- png_write_rows(png_ptr, row_pointers, n);
- g_free(to_free);
- r += n;
+ int number_of_passes = interlace ? png_set_interlace_handling(png_ptr) : 1;
+
+ for(int i=0;i<number_of_passes; ++i){
+ r = 0;
+ while (r < static_cast<png_uint_32>(height)) {
+ void *to_free;
+ int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data, color_type, bit_depth);
+ if (!n) break;
+ png_write_rows(png_ptr, row_pointers, n);
+ g_free(to_free);
+ r += n;
+ }
}
delete[] row_pointers;
@@ -308,7 +320,7 @@ sp_png_write_rgba_striped(SPDocument *doc,
*
*/
static int
-sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, void *data)
+sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, void *data, int color_type, int bit_depth)
{
struct SPEBP *ebp = (struct SPEBP *) data;
@@ -343,14 +355,17 @@ sp_export_get_rows(guchar const **rows, void **to_free, int row, int num_rows, v
ebp->drawing->render(dc, bbox);
cairo_surface_destroy(s);
- *to_free = px;
-
// PNG stores data as unpremultiplied big-endian RGBA, which means
// it's identical to the GdkPixbuf format.
convert_pixels_argb32_to_pixbuf(px, ebp->width, num_rows, stride);
+
+ *to_free = px;
- for (int r = 0; r < num_rows; r++) {
- rows[r] = px + r * stride;
+ // If a custom bit depth or color type is asked, then convert rgb to grayscale, etc.
+ if(color_type !=6 || bit_depth != 8){
+ const guchar* new_data = pixbuf_to_png(rows, px, num_rows, ebp->width, stride, color_type, bit_depth);
+ *to_free = (void*) new_data;
+ free(px);
}
return num_rows;
@@ -385,10 +400,10 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
unsigned long bgcolor,
unsigned int (*status) (float, void *),
void *data, bool force_overwrite,
- const std::vector<SPItem*> &items_only)
+ const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib)
{
return sp_export_png_file(doc, filename, Geom::Rect(Geom::Point(x0,y0),Geom::Point(x1,y1)),
- width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only);
+ width, height, xdpi, ydpi, bgcolor, status, data, force_overwrite, items_only, interlace, color_type, bit_depth, zlib);
}
ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
@@ -397,7 +412,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
unsigned long bgcolor,
unsigned (*status)(float, void *),
void *data, bool force_overwrite,
- const std::vector<SPItem*> &items_only)
+ const std::vector<SPItem*> &items_only, bool interlace, int color_type, int bit_depth, int zlib)
{
g_return_val_if_fail(doc != NULL, EXPORT_ERROR);
g_return_val_if_fail(filename != NULL, EXPORT_ERROR);
@@ -468,7 +483,7 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
ebp.px = g_try_new(guchar, 4 * ebp.sheight * width);
if (ebp.px) {
- write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp);
+ write_status = sp_png_write_rgba_striped(doc, filename, width, height, xdpi, ydpi, sp_export_get_rows, &ebp, interlace, color_type, bit_depth, zlib);
g_free(ebp.px);
}
diff --git a/src/helper/png-write.h b/src/helper/png-write.h
index 2657fb635..e47f01743 100644
--- a/src/helper/png-write.h
+++ b/src/helper/png-write.h
@@ -34,12 +34,14 @@ ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
double x0, double y0, double x1, double y1,
unsigned long int width, unsigned long int height, double xdpi, double ydpi,
unsigned long bgcolor,
- unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>());
+ unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>(),
+ bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6);
ExportResult sp_export_png_file(SPDocument *doc, gchar const *filename,
Geom::Rect const &area,
unsigned long int width, unsigned long int height, double xdpi, double ydpi,
unsigned long bgcolor,
- unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>());
+ unsigned int (*status) (float, void *), void *data, bool force_overwrite = false, const std::vector<SPItem*> &items_only = std::vector<SPItem*>(),
+ bool interlace = false, int color_type = 6, int bit_depth = 8, int zlib = 6);
#endif // SEEN_SP_PNG_WRITE_H
diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp
index 670e4c8b5..248714a1b 100644
--- a/src/ui/dialog/export.cpp
+++ b/src/ui/dialog/export.cpp
@@ -145,6 +145,12 @@ Export::Export (void) :
browse_image(Gtk::StockID(Gtk::Stock::INDEX), Gtk::ICON_SIZE_BUTTON),
batch_box(false, 5),
batch_export(_("B_atch export all selected objects"), _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)")),
+ interlacing(_("Use interlacing"),_("Enables ADAM7 interlacing for PNG output. This results in slightly heavier images, but big images will look better sooner when loading the file")),
+ bitdepth_label(_("Bit depth")),
+ bitdepth_cb(),
+ zlib_label(_("Compression")),
+ zlib_compression(),
+ phys_label(_("pHYs dpi")),
hide_box(false, 5),
hide_export(_("Hide a_ll except selected"), _("In the exported image, hide all objects except those that are selected")),
closeWhenDone(_("Close when complete"), _("Once the export completes, close this dialog")),
@@ -321,6 +327,34 @@ Export::Export (void) :
button_box.pack_start(closeWhenDone, true, true, 0 );
button_box.pack_end(export_button, false, false, 0);
+ /*Advanced*/
+ Gtk::Label *label_advanced = Gtk::manage(new Gtk::Label(_("Advanced"),1));
+ expander.set_label_widget(*label_advanced);
+ const char* const modes_list[]={"Gray_1", "Gray_2","Gray_4","Gray_8","Gray_16","RGB_8","RGB_16","GrayAlpha_8","GrayAlpha_16","RGBA_8","RGBA_16"};
+ for(int i=0; i<11; ++i)
+ bitdepth_cb.append(modes_list[i]);
+ bitdepth_cb.set_active_text("RGBA_8");
+ bitdepth_cb.set_hexpand();
+ const char* const zlist[]={"Z_NO_COMPRESSION","Z_BEST_SPEED","2","3","4","5","Z_DEFAULT_COMPRESSION","7","8","Z_BEST_COMPRESSION"};
+ for(int i=0; i<10; ++i)
+ zlib_compression.append(zlist[i]);
+ zlib_compression.set_active_text("Z_DEFAULT_COMPRESSION");
+ pHYs_adj = Gtk::Adjustment::create(0, 0, 100000, 0.1, 1.0, 0);
+ pHYs_sb = Gtk::SpinButton(pHYs_adj, 1.0, 2);
+ pHYs_sb.set_width_chars(7);
+ pHYs_sb.set_tooltip_text( _("Will force-set the physical dpi for the png file. Set this to 72 if you're planning to work on your png with Photoshop") );
+ zlib_compression.set_hexpand();
+ auto table = new Gtk::Grid();
+ gtk_container_add(GTK_CONTAINER(expander.gobj()), (GtkWidget*)(table->gobj()));
+ table->attach(interlacing,0,0,1,1);
+ table->attach(bitdepth_label,0,1,1,1);
+ table->attach(bitdepth_cb,1,1,1,1);
+ table->attach(zlib_label,0,2,1,1);
+ table->attach(zlib_compression,1,2,1,1);
+ table->attach(phys_label,0,3,1,1);
+ table->attach(pHYs_sb,1,3,1,1);
+ table->show();
+
/* Main dialog */
Gtk::Box *contents = _getContents();
contents->set_spacing(0);
@@ -329,6 +363,7 @@ Export::Export (void) :
contents->pack_start(hide_box);
contents->pack_end(button_box, false, 0);
contents->pack_end(_prog, Gtk::PACK_EXPAND_WIDGET);
+ contents->pack_end(expander, FALSE, FALSE,0);
/* Signal handlers */
filename_entry.signal_changed().connect( sigc::mem_fun(*this, &Export::onFilenameModified) );
@@ -943,6 +978,18 @@ void Export::onExport ()
bool exportSuccessful = false;
bool hide = hide_export.get_active ();
+
+ // Advanced parameters
+ bool do_interlace = (interlacing.get_active());
+ float pHYs = 0;
+ int zlib = zlib_compression.get_active_row_number() ;
+ const char* const modes_list[]={"Gray_1", "Gray_2","Gray_4","Gray_8","Gray_16","RGB_8","RGB_16","GrayAlpha_8","GrayAlpha_16","RGBA_8","RGBA_16"};
+ int colortypes[] = {0,0,0,0,0,2,2,4,4,6,6}; //keep in sync with modes_list in Export constructor. values are from libpng doc.
+ int bitdepths[] = {1,2,4,8,16,8,16,8,16,8,16};
+ int color_type = colortypes[bitdepth_cb.get_active_row_number()] ;
+ int bit_depth = bitdepths[bitdepth_cb.get_active_row_number()] ;
+
+
if (batch_export.get_active ()) {
// Batch export of selected objects
@@ -987,6 +1034,7 @@ void Export::onExport ()
if (dpi == 0.0) {
dpi = getValue(xdpi_adj);
}
+ pHYs = (pHYs_adj->get_value() > 0.01) ? pHYs_adj->get_value() : dpi;
Geom::OptRect area = item->desktopVisualBounds();
if (area) {
@@ -1003,11 +1051,12 @@ void Export::onExport ()
std::vector<SPItem*> x;
std::vector<SPItem*> selected(desktop->getSelection()->items().begin(), desktop->getSelection()->items().end());
if (!sp_export_png_file (doc, path.c_str(),
- *area, width, height, dpi, dpi,
+ *area, width, height, pHYs, pHYs,
nv->pagecolor,
onProgressCallback, (void*)prog_dlg,
TRUE, // overwrite without asking
- hide ? selected : x
+ hide ? selected : x,
+ do_interlace, color_type, bit_depth, zlib
)) {
gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
@@ -1049,6 +1098,7 @@ void Export::onExport ()
float const y1 = getValuePx(y1_adj);
float const xdpi = getValue(xdpi_adj);
float const ydpi = getValue(ydpi_adj);
+ pHYs = (pHYs_adj->get_value() > 0.01) ? pHYs_adj->get_value() : xdpi;
unsigned long int const width = int(getValue(bmwidth_adj) + 0.5);
unsigned long int const height = int(getValue(bmheight_adj) + 0.5);
@@ -1094,12 +1144,13 @@ void Export::onExport ()
std::vector<SPItem*> x;
std::vector<SPItem*> selected(desktop->getSelection()->items().begin(), desktop->getSelection()->items().end());
ExportResult status = sp_export_png_file(desktop->getDocument(), path.c_str(),
- Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, xdpi, ydpi,
+ Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)), width, height, pHYs, pHYs, //previously xdpi, ydpi.
nv->pagecolor,
onProgressCallback, (void*)prog_dlg,
FALSE,
- hide ? selected : x
- );
+ hide ? selected : x,
+ do_interlace, color_type, bit_depth, zlib
+ );
if (status == EXPORT_ERROR) {
gchar * safeFile = Inkscape::IO::sanitizeString(path.c_str());
gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h
index a1c44714b..84e1c2362 100644
--- a/src/ui/dialog/export.h
+++ b/src/ui/dialog/export.h
@@ -13,6 +13,9 @@
#define SP_EXPORT_H
#include <gtkmm/progressbar.h>
+#include <gtkmm/expander.h>
+#include <gtkmm/grid.h>
+#include <gtkmm/comboboxtext.h>
#include "ui/dialog/desktop-tracker.h"
#include "ui/widget/panel.h"
@@ -314,6 +317,17 @@ private:
Inkscape::UI::Widget::CheckButton closeWhenDone;
+ /* Advanced */
+ Gtk::Expander expander;
+ Inkscape::UI::Widget::CheckButton interlacing;
+ Gtk::Label bitdepth_label;
+ Gtk::ComboBoxText bitdepth_cb;
+ Gtk::Label zlib_label;
+ Gtk::ComboBoxText zlib_compression;
+ Gtk::Label phys_label;
+ Glib::RefPtr<Gtk::Adjustment> pHYs_adj;
+ Gtk::SpinButton pHYs_sb;
+
/* Export Button widgets */
Gtk::HBox button_box;
Gtk::Button export_button;