mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Cover Browser: Add an option to show covers with their original aspect ratio instead of resizing them all to have the same width and height. Option is in Preferences->Look & Feel->Cover Browser. Fixes #1295902 [[Enhancement Request] Allow cover browser to show book covers in actual dimensions/orientation](https://bugs.launchpad.net/calibre/+bug/1295902)
This commit is contained in:
parent
cfad8a5076
commit
eaae5b235e
@ -123,6 +123,7 @@ defs['cover_grid_texture'] = None
|
||||
defs['show_vl_tabs'] = False
|
||||
defs['show_highlight_toggle_button'] = False
|
||||
defs['add_comments_to_email'] = False
|
||||
defs['cb_preserve_aspect_ratio'] = False
|
||||
del defs
|
||||
# }}}
|
||||
|
||||
|
@ -21,6 +21,7 @@ pictureflow, pictureflowerror = plugins['pictureflow']
|
||||
if pictureflow is not None:
|
||||
|
||||
class EmptyImageList(pictureflow.FlowImages):
|
||||
|
||||
def __init__(self):
|
||||
pictureflow.FlowImages.__init__(self)
|
||||
|
||||
@ -108,7 +109,6 @@ if pictureflow is not None:
|
||||
def image(self, index):
|
||||
return self.model.cover(index)
|
||||
|
||||
|
||||
class CoverFlow(pictureflow.PictureFlow):
|
||||
|
||||
dc_signal = pyqtSignal()
|
||||
@ -125,6 +125,10 @@ if pictureflow is not None:
|
||||
type=Qt.QueuedConnection)
|
||||
self.context_menu = None
|
||||
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||
try:
|
||||
self.setPreserveAspectRatio(gprefs['cb_preserve_aspect_ratio'])
|
||||
except AttributeError:
|
||||
pass # source checkout without updated binary
|
||||
if hasattr(self, 'setSubtitleFont'):
|
||||
self.setSubtitleFont(QFont(rating_font()))
|
||||
if not gprefs['cover_browser_reflections']:
|
||||
@ -290,7 +294,6 @@ class CoverFlowMixin(object):
|
||||
self.library_view.setCurrentIndex(idx)
|
||||
self.library_view.scroll_to_row(idx.row())
|
||||
|
||||
|
||||
def show_cover_browser(self):
|
||||
d = CBDialog(self, self.cover_flow)
|
||||
d.addAction(self.cb_splitter.action_toggle)
|
||||
@ -313,7 +316,6 @@ class CoverFlowMixin(object):
|
||||
self.cb_dialog = None
|
||||
self.cb_splitter.button.set_state_to_show()
|
||||
|
||||
|
||||
def sync_cf_to_listview(self, current, previous):
|
||||
if self.cover_flow_sync_flag and self.cover_flow.isVisible() and \
|
||||
self.cover_flow.currentSlide() != current.row():
|
||||
|
@ -318,6 +318,9 @@ struct SlideInfo
|
||||
PFreal cy;
|
||||
};
|
||||
|
||||
static const QString OFFSET_KEY("offset");
|
||||
static const QString WIDTH_KEY("width");
|
||||
|
||||
// PicturePlowPrivate {{{
|
||||
|
||||
class PictureFlowPrivate
|
||||
@ -367,6 +370,7 @@ public:
|
||||
QTime previousPosTimestamp;
|
||||
int pixelDistanceMoved;
|
||||
int pixelsToMovePerSlide;
|
||||
bool preserveAspectRatio;
|
||||
QFont subtitleFont;
|
||||
|
||||
void setImages(FlowImages *images);
|
||||
@ -421,6 +425,7 @@ PictureFlowPrivate::PictureFlowPrivate(PictureFlow* w, int queueLength_)
|
||||
slideHeight = 200;
|
||||
fontSize = 10;
|
||||
doReflections = true;
|
||||
preserveAspectRatio = false;
|
||||
|
||||
centerIndex = 0;
|
||||
queueLength = queueLength_;
|
||||
@ -598,41 +603,58 @@ void PictureFlowPrivate::resetSlides()
|
||||
}
|
||||
}
|
||||
|
||||
static QImage prepareSurface(QImage img, int w, int h, bool doReflections)
|
||||
static QImage prepareSurface(QImage srcimg, int w, int h, bool doReflections, bool preserveAspectRatio)
|
||||
{
|
||||
img = img.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
// slightly larger, to accommodate for the reflection
|
||||
int hs = int(h * REFLECTION_FACTOR), left = 0, top = 0, a = 0, r = 0, g = 0, b = 0, ht, x, y, bpp;
|
||||
QImage img = (preserveAspectRatio) ? QImage(w, h, srcimg.format()) : srcimg.scaled(w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
QRgb color;
|
||||
|
||||
// slightly larger, to accommodate for the reflection
|
||||
int hs = int(h * REFLECTION_FACTOR);
|
||||
// offscreen buffer: black is sweet
|
||||
QImage result(hs, w, QImage::Format_RGB16);
|
||||
result.fill(0);
|
||||
|
||||
// offscreen buffer: black is sweet
|
||||
QImage result(hs, w, QImage::Format_RGB16);
|
||||
result.fill(0);
|
||||
|
||||
// transpose the image, this is to speed-up the rendering
|
||||
// because we process one column at a time
|
||||
// (and much better and faster to work row-wise, i.e in one scanline)
|
||||
for(int x = 0; x < w; x++)
|
||||
for(int y = 0; y < h; y++)
|
||||
result.setPixel(y, x, img.pixel(x, y));
|
||||
|
||||
if (doReflections) {
|
||||
// create the reflection
|
||||
int ht = hs - h;
|
||||
for(int x = 0; x < w; x++)
|
||||
for(int y = 0; y < ht; y++)
|
||||
{
|
||||
QRgb color = img.pixel(x, img.height()-y-1);
|
||||
//QRgb565 color = img.scanLine(img.height()-y-1) + x*sizeof(QRgb565); //img.pixel(x, img.height()-y-1);
|
||||
int a = qAlpha(color);
|
||||
int r = qRed(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
int g = qGreen(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
int b = qBlue(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
result.setPixel(h+y, x, qRgb(r, g, b));
|
||||
if (preserveAspectRatio) {
|
||||
QImage temp = srcimg.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
img = QImage(w, h, temp.format());
|
||||
img.fill(0);
|
||||
left = (w - temp.width()) / 2;
|
||||
top = h - temp.height();
|
||||
bpp = img.bytesPerLine() / img.width();
|
||||
x = temp.width() * bpp;
|
||||
result.setText(OFFSET_KEY, QString::number(left));
|
||||
result.setText(WIDTH_KEY, QString::number(temp.width()));
|
||||
for (y = 0; y < temp.height(); y++) {
|
||||
const uchar *src = temp.scanLine(y);
|
||||
uchar *dest = img.scanLine(top + y) + (bpp * left);
|
||||
memcpy(dest, src, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
// transpose the image, this is to speed-up the rendering
|
||||
// because we process one column at a time
|
||||
// (and much better and faster to work row-wise, i.e in one scanline)
|
||||
for(x = 0; x < w; x++)
|
||||
for(y = 0; y < h; y++)
|
||||
result.setPixel(y, x, img.pixel(x, y));
|
||||
|
||||
if (doReflections) {
|
||||
// create the reflection
|
||||
ht = hs - h;
|
||||
for(x = 0; x < w; x++)
|
||||
for(y = 0; y < ht; y++)
|
||||
{
|
||||
color = img.pixel(x, img.height()-y-1);
|
||||
//QRgb565 color = img.scanLine(img.height()-y-1) + x*sizeof(QRgb565); //img.pixel(x, img.height()-y-1);
|
||||
a = qAlpha(color);
|
||||
r = qRed(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
g = qGreen(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
b = qBlue(color) * a / 256 * (ht - y) / ht * 3/5;
|
||||
result.setPixel(h+y, x, qRgb(r, g, b));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -668,12 +690,12 @@ QImage* PictureFlowPrivate::surface(int slideIndex)
|
||||
painter.setBrush(QBrush());
|
||||
painter.drawRect(2, 2, slideWidth-3, slideHeight-3);
|
||||
painter.end();
|
||||
blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight, doReflections);
|
||||
blankSurface = prepareSurface(blankSurface, slideWidth, slideHeight, doReflections, preserveAspectRatio);
|
||||
}
|
||||
return &blankSurface;
|
||||
}
|
||||
|
||||
surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight, doReflections)));
|
||||
surfaceCache.insert(slideIndex, new QImage(prepareSurface(img, slideWidth, slideHeight, doReflections, preserveAspectRatio)));
|
||||
return surfaceCache[slideIndex];
|
||||
}
|
||||
|
||||
@ -874,8 +896,7 @@ QRect PictureFlowPrivate::renderCenterSlide(const SlideInfo &slide) {
|
||||
// Renders a slide to offscreen buffer. Returns a rect of the rendered area.
|
||||
// alpha=256 means normal, alpha=0 is fully black, alpha=128 half transparent
|
||||
// col1 and col2 limit the column for rendering.
|
||||
QRect PictureFlowPrivate::renderSlide(const SlideInfo &slide, int alpha,
|
||||
int col1, int col2)
|
||||
QRect PictureFlowPrivate::renderSlide(const SlideInfo &slide, int alpha, int col1, int col2)
|
||||
{
|
||||
QImage* src = surface(slide.slideIndex);
|
||||
if(!src)
|
||||
@ -913,6 +934,13 @@ int col1, int col2)
|
||||
|
||||
bool flag = false;
|
||||
rect.setLeft(xi);
|
||||
int img_offset = 0, img_width = 0;
|
||||
bool slide_moving_to_center = false;
|
||||
if (preserveAspectRatio) {
|
||||
img_offset = src->text(OFFSET_KEY).toInt();
|
||||
img_width = src->text(WIDTH_KEY).toInt();
|
||||
slide_moving_to_center = slide.slideIndex == target && target != centerIndex;
|
||||
}
|
||||
for(int x = qMax(xi, col1); x <= col2; x++)
|
||||
{
|
||||
PFreal hity = 0;
|
||||
@ -935,6 +963,17 @@ int col1, int col2)
|
||||
break;
|
||||
if(column < 0)
|
||||
continue;
|
||||
if (preserveAspectRatio && !slide_moving_to_center) {
|
||||
// We dont want a black border at the edge of narrow images when the images are in the left or right stacks
|
||||
if (slide.slideIndex < centerIndex) {
|
||||
column = qMin(column + img_offset, sw - 1);
|
||||
} else if (slide.slideIndex == centerIndex) {
|
||||
if (target > centerIndex) column = qMin(column + img_offset, sw - 1);
|
||||
else if (target < centerIndex) column = qMax(column - sw + img_offset + img_width, 0);
|
||||
} else {
|
||||
column = qMax(column - sw + img_offset + img_width, 0);
|
||||
}
|
||||
}
|
||||
|
||||
rect.setRight(x);
|
||||
if(!flag)
|
||||
@ -1196,6 +1235,17 @@ void PictureFlow::setSlideSize(QSize size)
|
||||
d->setSlideSize(size);
|
||||
}
|
||||
|
||||
bool PictureFlow::preserveAspectRatio() const
|
||||
{
|
||||
return d->preserveAspectRatio;
|
||||
}
|
||||
|
||||
void PictureFlow::setPreserveAspectRatio(bool preserve)
|
||||
{
|
||||
d->preserveAspectRatio = preserve;
|
||||
clearCaches();
|
||||
}
|
||||
|
||||
void PictureFlow::setSubtitleFont(QFont font)
|
||||
{
|
||||
d->subtitleFont = font;
|
||||
|
@ -93,6 +93,7 @@ Q_OBJECT
|
||||
Q_PROPERTY(int currentSlide READ currentSlide WRITE setCurrentSlide)
|
||||
Q_PROPERTY(QSize slideSize READ slideSize WRITE setSlideSize)
|
||||
Q_PROPERTY(QFont subtitleFont READ subtitleFont WRITE setSubtitleFont)
|
||||
Q_PROPERTY(bool preserveAspectRatio READ preserveAspectRatio WRITE setPreserveAspectRatio)
|
||||
|
||||
public:
|
||||
/*!
|
||||
@ -121,6 +122,16 @@ public:
|
||||
*/
|
||||
void setSlideSize(QSize size);
|
||||
|
||||
/*!
|
||||
Returns whether aspect ration is preserved when scaling images
|
||||
*/
|
||||
bool preserveAspectRatio() const;
|
||||
|
||||
/*!
|
||||
Whether to preserve aspect ration when scaling images
|
||||
*/
|
||||
void setPreserveAspectRatio(bool preserve);
|
||||
|
||||
/*!
|
||||
Turn the reflections on/off.
|
||||
*/
|
||||
|
@ -41,6 +41,10 @@ public :
|
||||
|
||||
void setSlideSize(QSize size);
|
||||
|
||||
bool preserveAspectRatio() const;
|
||||
|
||||
void setPreserveAspectRatio(bool preserve);
|
||||
|
||||
QFont subtitleFont() const;
|
||||
|
||||
void setSubtitleFont(QFont font);
|
||||
|
@ -183,6 +183,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
r('use_roman_numerals_for_series_number', config)
|
||||
r('separate_cover_flow', config, restart_required=True)
|
||||
r('cb_fullscreen', gprefs)
|
||||
r('cb_preserve_aspect_ratio', gprefs)
|
||||
|
||||
choices = [(_('Off'), 'off'), (_('Small'), 'small'),
|
||||
(_('Medium'), 'medium'), (_('Large'), 'large')]
|
||||
@ -461,6 +462,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
||||
gui.library_view.refresh_book_details()
|
||||
if hasattr(gui.cover_flow, 'setShowReflections'):
|
||||
gui.cover_flow.setShowReflections(gprefs['cover_browser_reflections'])
|
||||
gui.cover_flow.setPreserveAspectRatio(gprefs['cb_preserve_aspect_ratio'])
|
||||
gui.library_view.refresh_row_sizing()
|
||||
gui.grid_view.refresh_settings()
|
||||
|
||||
|
@ -897,7 +897,7 @@ a few top-level elements.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<item row="6" column="0" colspan="2">
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@ -913,14 +913,14 @@ a few top-level elements.</string>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="opt_cover_flow_queue_length"/>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_cb_fullscreen">
|
||||
<property name="text">
|
||||
<string>When showing cover browser in separate window, show it &fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QLabel" name="fs_help_msg">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin-left: 1.5em</string>
|
||||
@ -940,6 +940,17 @@ a few top-level elements.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_cb_preserve_aspect_ratio">
|
||||
<property name="toolTip">
|
||||
<string>Show covers in their original aspect ratio instead of resizing
|
||||
them to all have the same width and height</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Preserve &aspect ratio of covers displayed in the cover browser</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
Loading…
x
Reference in New Issue
Block a user