Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""":mod:`wand.color` --- Colors 

2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

3 

4.. versionadded:: 0.1.2 

5 

6""" 

7import ctypes 

8import numbers 

9 

10from .api import library 

11from .cdefs.structures import MagickPixelPacket, PixelInfo 

12from .compat import binary, text 

13from .resource import Resource 

14from .version import MAGICK_VERSION_NUMBER, MAGICK_HDRI, QUANTUM_DEPTH 

15 

16__all__ = 'Color', 'scale_quantum_to_int8' 

17 

18 

19class Color(Resource): 

20 """Color value. 

21 

22 Unlike any other objects in Wand, its resource management can be 

23 implicit when it used outside of :keyword:`with` block. In these case, 

24 its resource are allocated for every operation which requires a resource 

25 and destroyed immediately. Of course it is inefficient when the 

26 operations are much, so to avoid it, you should use color objects 

27 inside of :keyword:`with` block explicitly e.g.:: 

28 

29 red_count = 0 

30 with Color('#f00') as red: 

31 with Image(filename='image.png') as img: 

32 for row in img: 

33 for col in row: 

34 if col == red: 

35 red_count += 1 

36 

37 :param string: a color name string e.g. ``'rgb(255, 255, 255)'``, 

38 ``'#fff'``, ``'white'``. see `ImageMagick Color Names`_ 

39 doc also 

40 :type string: :class:`basestring` 

41 

42 .. versionchanged:: 0.3.0 

43 :class:`Color` objects become hashable. 

44 

45 .. versionchanged:: 0.5.1 

46 Color channel properties can now be set. 

47 

48 .. versionchanged:: 0.5.1 

49 Added :attr:`cyan`, :attr:`magenta`, :attr:`yellow`, & :attr:`black` 

50 properties for CMYK :class:`Color` instances. 

51 

52 .. versionchanged:: 0.5.1 

53 Method :meth:`Color.from_hsl()` can create a RGB color from ``hue``, 

54 ``saturation``, & ``lightness`` values. 

55 

56 .. seealso:: 

57 

58 `ImageMagick Color Names`_ 

59 The color can then be given as a color name (there is a limited 

60 but large set of these; see below) or it can be given as a set 

61 of numbers (in decimal or hexadecimal), each corresponding to 

62 a channel in an RGB or RGBA color model. HSL, HSLA, HSB, HSBA, 

63 CMYK, or CMYKA color models may also be specified. These topics 

64 are briefly described in the sections below. 

65 

66 .. _ImageMagick Color Names: http://www.imagemagick.org/script/color.php 

67 

68 .. describe:: == (other) 

69 

70 Equality operator. 

71 

72 :param other: a color another one 

73 :type color: :class:`Color` 

74 :returns: ``True`` only if two images equal. 

75 :rtype: :class:`bool` 

76 

77 """ 

78 

79 #: (:class:`bool`) Whether the color has changed or not. 

80 dirty = None 

81 

82 c_is_resource = library.IsPixelWand 

83 c_destroy_resource = library.DestroyPixelWand 

84 c_get_exception = library.PixelGetException 

85 c_clear_exception = library.PixelClearException 

86 

87 __slots__ = 'raw', 'c_resource', 'allocated' 

88 

89 def __init__(self, string=None, raw=None): 

90 if (string is None and raw is None or 

91 string is not None and raw is not None): 

92 raise TypeError('expected one argument') 

93 

94 # MagickPixelPacket has been deprecated, use PixelInfo 

95 self.use_pixel = MAGICK_VERSION_NUMBER >= 0x700 

96 self.dirty = False 

97 self.allocated = 0 

98 if raw is None: 

99 if self.use_pixel: # pragma: no cover 

100 self.raw = ctypes.create_string_buffer( 

101 ctypes.sizeof(PixelInfo) 

102 ) 

103 else: 

104 self.raw = ctypes.create_string_buffer( 

105 ctypes.sizeof(MagickPixelPacket) 

106 ) 

107 with self: 

108 # Create color from string. 

109 ok = library.PixelSetColor(self.resource, binary(string)) 

110 if not ok: 

111 # Could not understand color-input. Try sending 

112 # ImageMagick's exception. 

113 self.raise_exception() 

114 # That might be only a warning. Try a more generic message. 

115 msg = 'Unrecognized color string "{0}"'.format(string) 

116 raise ValueError(msg) 

117 # Copy color value to structure buffer for future read. 

118 library.PixelGetMagickColor(self.resource, self.raw) 

119 else: 

120 self.raw = raw 

121 

122 def __getinitargs__(self): 

123 return self.string, None 

124 

125 def __enter__(self): 

126 if self.allocated < 1: 

127 with self.allocate(): 

128 # Initialize resource. 

129 self.resource = library.NewPixelWand() 

130 # Restore color value from structure buffer. 

131 if self.use_pixel: # pragma: no cover 

132 library.PixelSetPixelColor(self.resource, self.raw) 

133 else: 

134 library.PixelSetMagickColor(self.resource, self.raw) 

135 self.allocated = 1 

136 else: 

137 self.allocated += 1 

138 return Resource.__enter__(self) 

139 

140 def __exit__(self, type, value, traceback): 

141 self.allocated -= 1 

142 if self.dirty: 

143 library.PixelGetMagickColor(self.resource, self.raw) 

144 self.dirty = False 

145 if self.allocated < 1: 

146 Resource.__exit__(self, type, value, traceback) 

147 

148 def __eq__(self, other): 

149 if not isinstance(other, Color): 

150 return False 

151 with self as this: 

152 with other: 

153 return self.c_equals(this.resource, other.resource) 

154 

155 def __ne__(self, other): 

156 return not (self == other) 

157 

158 def __hash__(self): 

159 if self.alpha: 

160 return hash(self.normalized_string) 

161 return hash(None) 

162 

163 def __str__(self): 

164 return self.string 

165 

166 def __repr__(self): 

167 c = type(self) 

168 return '{0}.{1}({2!r})'.format(c.__module__, c.__name__, self.string) 

169 

170 def _assert_double(self, subject): 

171 """Ensure the given ``subject`` is a float type, and value between 

172 0.0 & 1.0. 

173 

174 :param subject: value to assert as a valid double. 

175 :type subject: :class:`numbers.Real` 

176 :raises ValueError: if the subject is not between 0.0 and 1.0 

177 :raises TypeError: if the subject is not a float-point number. 

178 

179 ..versionadded:: 0.5.1 

180 """ 

181 if not isinstance(subject, numbers.Real): 

182 raise TypeError('Expecting a float-point real number, not ' + 

183 repr(subject)) 

184 if subject < 0.0 or subject > 1.0: 

185 raise ValueError('Expecting a real number between 0.0 & 1.0, not' + 

186 repr(subject)) 

187 

188 def _assert_int8(self, subject): 

189 """Ensure the given ``subject`` is a integer type, and value between 

190 0 & 255. 

191 

192 :param subject: value to assert as a valid number. 

193 :type subject: :class:`numbers.Integral` 

194 :raises ValueError: if the subject is not between 0 and 255 

195 :raises TypeError: if the subject is not a Integral number. 

196 

197 ..versionadded:: 0.5.1 

198 """ 

199 if not isinstance(subject, numbers.Integral): 

200 raise TypeError('Expecting an integer number, not ' + 

201 repr(subject)) 

202 if subject < 0 or subject > 255: 

203 raise ValueError('Expecting a real number between 0 & 255, not' + 

204 repr(subject)) 

205 

206 def _assert_quantum(self, subject): 

207 """Ensure the given ``subject`` is a number, and value between 

208 0.0 & QuantumRange. 

209 

210 The QuantumRange is the max value based on the QuantumDepth of the 

211 ImageMagick library (i.e. Q16). 

212 

213 :param subject: value to assert as a valid double. 

214 :type subject: :class:`numbers.Number` 

215 :raises ValueError: if the subject is not between 0 and QuantumRange 

216 :raises TypeError: if the subject is not a number. 

217 

218 ..versionadded:: 0.5.1 

219 """ 

220 quantum_range = { 

221 8: 255.0, 

222 16: 65535.0, 

223 32: 4294967295.0, 

224 64: 18446744073709551615.0 

225 } 

226 if not isinstance(subject, numbers.Number): 

227 raise TypeError('Expecting a number, not ' + repr(subject)) 

228 if subject < 0.0 or subject > quantum_range[QUANTUM_DEPTH]: 

229 message = 'Expecting a number between 0 & {0}, not {1}' 

230 raise ValueError(message.format(quantum_range[QUANTUM_DEPTH], 

231 repr(subject))) 

232 

233 def _repr_html_(self): 

234 html = """ 

235 <span style="background-color:#{red:02X}{green:02X}{blue:02X}; 

236 display:inline-block; 

237 line-height:1em; 

238 width:1em;">&nbsp;</span> 

239 <strong>#{red:02X}{green:02X}{blue:02X}</strong> 

240 """ 

241 return html.format(red=self.red_int8, 

242 green=self.green_int8, 

243 blue=self.blue_int8) 

244 

245 @staticmethod 

246 def c_equals(a, b): 

247 """Raw level version of equality test function for two pixels. 

248 

249 :param a: a pointer to PixelWand to compare 

250 :type a: :class:`ctypes.c_void_p` 

251 :param b: a pointer to PixelWand to compare 

252 :type b: :class:`ctypes.c_void_p` 

253 :returns: ``True`` only if two pixels equal 

254 :rtype: :class:`bool` 

255 

256 .. note:: 

257 

258 It's only for internal use. Don't use it directly. 

259 Use ``==`` operator of :class:`Color` instead. 

260 

261 """ 

262 alpha = library.PixelGetAlpha 

263 return bool(library.IsPixelWandSimilar(a, b, 0) and 

264 alpha(a) == alpha(b)) 

265 

266 @classmethod 

267 def from_hsl(cls, hue=0.0, saturation=0.0, lightness=0.0): 

268 """Creates a RGB color from HSL values. The ``hue``, ``saturation``, 

269 and ``lightness`` must be normalized between 0.0 & 1.0. 

270 

271 .. code:: 

272 

273 h=0.75 # 270 Degrees 

274 s=1.0 # 100 Percent 

275 l=0.5 # 50 Percent 

276 with Color.from_hsl(hue=h, saturation=s, lightness=l) as color: 

277 print(color) #=> srgb(128,0,255) 

278 

279 :param hue: a normalized double between 0.0 & 1.0. 

280 :type hue: :class:`numbers.Real` 

281 :param saturation: a normalized double between 0.0 & 1.0. 

282 :type saturation: :class:`numbers.Real` 

283 :param lightness: a normalized double between 0.0 & 1.0. 

284 :type lightness: :class:`numbers.Real` 

285 :rtype: :class:`Color` 

286 

287 .. versionadded:: 0.5.1 

288 """ 

289 color = cls('WHITE') 

290 color._assert_double(hue) 

291 color._assert_double(saturation) 

292 color._assert_double(lightness) 

293 color.dirty = True 

294 with color: 

295 library.PixelSetHSL(color.resource, hue, saturation, lightness) 

296 return color 

297 

298 @classmethod 

299 def from_pixelwand(cls, pixelwand): 

300 assert pixelwand 

301 if MAGICK_VERSION_NUMBER < 0x700: 

302 pixel_structure = MagickPixelPacket 

303 else: # pragma: no cover 

304 pixel_structure = PixelInfo 

305 size = ctypes.sizeof(pixel_structure) 

306 raw_buffer = ctypes.create_string_buffer(size) 

307 library.PixelGetMagickColor(pixelwand, raw_buffer) 

308 return cls(raw=raw_buffer) 

309 

310 @property 

311 def alpha(self): 

312 """(:class:`numbers.Real`) Alpha value, from 0.0 to 1.0.""" 

313 with self: 

314 return library.PixelGetAlpha(self.resource) 

315 

316 @alpha.setter 

317 def alpha(self, value): 

318 self._assert_double(value) 

319 self.dirty = True 

320 with self: 

321 library.PixelSetAlpha(self.resource, value) 

322 

323 @property 

324 def alpha_int8(self): 

325 """(:class:`numbers.Integral`) Alpha value as 8bit integer which is 

326 a common style. From 0 to 255. 

327 

328 .. versionadded:: 0.3.0 

329 

330 """ 

331 return scale_quantum_to_int8(self.alpha_quantum) 

332 

333 @alpha_int8.setter 

334 def alpha_int8(self, value): 

335 self._assert_int8(value) 

336 self.alpha = float(value) / 255.0 

337 

338 @property 

339 def alpha_quantum(self): 

340 """(:class:`numbers.Integral`) Alpha value. 

341 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

342 

343 .. versionadded:: 0.3.0 

344 

345 """ 

346 with self: 

347 return library.PixelGetAlphaQuantum(self.resource) 

348 

349 @alpha_quantum.setter 

350 def alpha_quantum(self, value): 

351 self._assert_quantum(value) 

352 self.dirty = True 

353 with self: 

354 library.PixelSetAlphaQuantum(self.resource, value) 

355 

356 @property 

357 def black(self): 

358 """(:class:`numbers.Real`) Black, or ``'K'``, color channel in CMYK 

359 colorspace. Unused by RGB colorspace. 

360 

361 .. versionadded:: 0.5.1 

362 """ 

363 with self: 

364 return library.PixelGetBlack(self.resource) 

365 

366 @black.setter 

367 def black(self, value): 

368 self._assert_double(value) 

369 self.dirty = True 

370 with self: 

371 library.PixelSetBlack(self.resource, value) 

372 

373 @property 

374 def black_int8(self): 

375 """(:class:`numbers.Integral`) Black value as 8bit integer which is 

376 a common style. From 0 to 255. 

377 

378 .. versionadded:: 0.5.1 

379 """ 

380 return scale_quantum_to_int8(self.black_quantum) 

381 

382 @black_int8.setter 

383 def black_int8(self, value): 

384 self._assert_int8(value) 

385 self.black = float(value) / 255.0 

386 

387 @property 

388 def black_quantum(self): 

389 """(:class:`numbers.Integral`) Black. 

390 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

391 

392 .. versionadded:: 0.5.1 

393 """ 

394 with self: 

395 return library.PixelGetBlackQuantum(self.resource) 

396 

397 @black_quantum.setter 

398 def black_quantum(self, value): 

399 self._assert_quantum(value) 

400 self.dirty = True 

401 with self: 

402 library.PixelSetBlackQuantum(self.resource, value) 

403 

404 @property 

405 def blue(self): 

406 """(:class:`numbers.Real`) Blue, from 0.0 to 1.0.""" 

407 with self: 

408 return library.PixelGetBlue(self.resource) 

409 

410 @blue.setter 

411 def blue(self, value): 

412 self._assert_double(value) 

413 self.dirty = True 

414 with self: 

415 library.PixelSetBlue(self.resource, value) 

416 

417 @property 

418 def blue_int8(self): 

419 """(:class:`numbers.Integral`) Blue as 8bit integer which is 

420 a common style. From 0 to 255. 

421 

422 .. versionadded:: 0.3.0 

423 

424 """ 

425 return scale_quantum_to_int8(self.blue_quantum) 

426 

427 @blue_int8.setter 

428 def blue_int8(self, value): 

429 self._assert_int8(value) 

430 self.blue = float(value) / 255.0 

431 

432 @property 

433 def blue_quantum(self): 

434 """(:class:`numbers.Integral`) Blue. 

435 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

436 

437 .. versionadded:: 0.3.0 

438 

439 """ 

440 with self: 

441 return library.PixelGetBlueQuantum(self.resource) 

442 

443 @blue_quantum.setter 

444 def blue_quantum(self, value): 

445 self._assert_quantum(value) 

446 self.dirty = True 

447 with self: 

448 library.PixelSetBlueQuantum(self.resource, value) 

449 

450 @property 

451 def cyan(self): 

452 """(:class:`numbers.Real`) Cyan color channel in CMYK 

453 colorspace. Unused by RGB colorspace. 

454 

455 .. versionadded:: 0.5.1 

456 """ 

457 with self: 

458 return library.PixelGetCyan(self.resource) 

459 

460 @cyan.setter 

461 def cyan(self, value): 

462 self._assert_double(value) 

463 self.dirty = True 

464 with self: 

465 library.PixelSetCyan(self.resource, value) 

466 

467 @property 

468 def cyan_int8(self): 

469 """(:class:`numbers.Integral`) Cyan value as 8bit integer which is 

470 a common style. From 0 to 255. 

471 

472 .. versionadded:: 0.5.1 

473 """ 

474 return scale_quantum_to_int8(self.cyan_quantum) 

475 

476 @cyan_int8.setter 

477 def cyan_int8(self, value): 

478 self._assert_int8(value) 

479 self.cyan = float(value) / 255.0 

480 

481 @property 

482 def cyan_quantum(self): 

483 """(:class:`numbers.Integral`) Cyan. 

484 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

485 

486 .. versionadded:: 0.5.1 

487 """ 

488 with self: 

489 return library.PixelGetCyanQuantum(self.resource) 

490 

491 @cyan_quantum.setter 

492 def cyan_quantum(self, value): 

493 self._assert_quantum(value) 

494 self.dirty = True 

495 with self: 

496 library.PixelSetCyanQuantum(self.resource, value) 

497 

498 @property 

499 def fuzz(self): 

500 with self: 

501 return library.PixelGetFuzz(self.resource) 

502 

503 @fuzz.setter 

504 def fuzz(self, value): 

505 if not isinstance(value, numbers.Real): 

506 raise TypeError('Expecting a float-point real number, not ' + 

507 repr(value)) 

508 self.dirty = True 

509 with self: 

510 library.PixelSetFuzz(self.resource, value) 

511 

512 @property 

513 def green(self): 

514 """(:class:`numbers.Real`) Green, from 0.0 to 1.0.""" 

515 with self: 

516 return library.PixelGetGreen(self.resource) 

517 

518 @green.setter 

519 def green(self, value): 

520 self._assert_double(value) 

521 self.dirty = True 

522 with self: 

523 library.PixelSetGreen(self.resource, value) 

524 

525 @property 

526 def green_int8(self): 

527 """(:class:`numbers.Integral`) Green as 8bit integer which is 

528 a common style. From 0 to 255. 

529 

530 .. versionadded:: 0.3.0 

531 

532 """ 

533 return scale_quantum_to_int8(self.green_quantum) 

534 

535 @green_int8.setter 

536 def green_int8(self, value): 

537 self._assert_int8(value) 

538 self.green = float(value) / 255.0 

539 

540 @property 

541 def green_quantum(self): 

542 """(:class:`numbers.Integral`) Green. 

543 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

544 

545 .. versionadded:: 0.3.0 

546 

547 """ 

548 with self: 

549 return library.PixelGetGreenQuantum(self.resource) 

550 

551 @green_quantum.setter 

552 def green_quantum(self, value): 

553 self._assert_quantum(value) 

554 self.dirty = True 

555 with self: 

556 library.PixelSetGreenQuantum(self.resource, value) 

557 

558 @property 

559 def magenta(self): 

560 """(:class:`numbers.Real`) Magenta color channel in CMYK 

561 colorspace. Unused by RGB colorspace. 

562 

563 .. versionadded:: 0.5.1 

564 """ 

565 with self: 

566 return library.PixelGetMagenta(self.resource) 

567 

568 @magenta.setter 

569 def magenta(self, value): 

570 self._assert_double(value) 

571 self.dirty = True 

572 with self: 

573 library.PixelSetMagenta(self.resource, value) 

574 

575 @property 

576 def magenta_int8(self): 

577 """(:class:`numbers.Integral`) Magenta value as 8bit integer which is 

578 a common style. From 0 to 255. 

579 

580 .. versionadded:: 0.5.1 

581 """ 

582 return scale_quantum_to_int8(self.magenta_quantum) 

583 

584 @magenta_int8.setter 

585 def magenta_int8(self, value): 

586 self._assert_int8(value) 

587 self.magenta = float(value) / 255.0 

588 

589 @property 

590 def magenta_quantum(self): 

591 with self: 

592 return library.PixelGetMagentaQuantum(self.resource) 

593 

594 @magenta_quantum.setter 

595 def magenta_quantum(self, value): 

596 """(:class:`numbers.Integral`) Magenta. 

597 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

598 

599 .. versionadded:: 0.5.1 

600 """ 

601 self._assert_quantum(value) 

602 self.dirty = True 

603 with self: 

604 library.PixelSetMagentaQuantum(self.resource, value) 

605 

606 @property 

607 def normalized_string(self): 

608 """(:class:`basestring`) The normalized string representation of 

609 the color. The same color is always represented to the same 

610 string. 

611 

612 .. versionadded:: 0.3.0 

613 

614 """ 

615 with self: 

616 string = library.PixelGetColorAsNormalizedString(self.resource) 

617 return text(string.value) 

618 

619 @property 

620 def red(self): 

621 """(:class:`numbers.Real`) Red, from 0.0 to 1.0.""" 

622 with self: 

623 return library.PixelGetRed(self.resource) 

624 

625 @red.setter 

626 def red(self, value): 

627 self._assert_double(value) 

628 self.dirty = True 

629 with self: 

630 library.PixelSetRed(self.resource, value) 

631 

632 @property 

633 def red_int8(self): 

634 """(:class:`numbers.Integral`) Red as 8bit integer which is a common 

635 style. From 0 to 255. 

636 

637 .. versionadded:: 0.3.0 

638 

639 """ 

640 return scale_quantum_to_int8(self.red_quantum) 

641 

642 @red_int8.setter 

643 def red_int8(self, value): 

644 self._assert_int8(value) 

645 self.red = float(value) / 255.0 

646 

647 @property 

648 def red_quantum(self): 

649 """(:class:`numbers.Integral`) Red. 

650 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

651 

652 .. versionadded:: 0.3.0 

653 

654 """ 

655 with self: 

656 return library.PixelGetRedQuantum(self.resource) 

657 

658 @red_quantum.setter 

659 def red_quantum(self, value): 

660 self._assert_quantum(value) 

661 self.dirty = True 

662 with self: 

663 library.PixelSetRedQuantum(self.resource, value) 

664 

665 @property 

666 def string(self): 

667 """(:class:`basestring`) The string representation of the color.""" 

668 with self: 

669 color_string = library.PixelGetColorAsString(self.resource) 

670 return text(color_string.value) 

671 

672 @property 

673 def yellow(self): 

674 """(:class:`numbers.Real`) Yellow color channel in CMYK 

675 colorspace. Unused by RGB colorspace. 

676 

677 .. versionadded:: 0.5.1 

678 """ 

679 with self: 

680 return library.PixelGetYellow(self.resource) 

681 

682 @yellow.setter 

683 def yellow(self, value): 

684 self._assert_double(value) 

685 self.dirty = True 

686 with self: 

687 library.PixelSetYellow(self.resource, value) 

688 

689 @property 

690 def yellow_int8(self): 

691 """(:class:`numbers.Integral`) Yellow as 8bit integer which is a common 

692 style. From 0 to 255. 

693 

694 .. versionadded:: 0.5.1 

695 """ 

696 return scale_quantum_to_int8(self.yellow_quantum) 

697 

698 @yellow_int8.setter 

699 def yellow_int8(self, value): 

700 self._assert_int8(value) 

701 self.yellow = float(value) / 255.0 

702 

703 @property 

704 def yellow_quantum(self): 

705 """(:class:`numbers.Integral`) Yellow. 

706 Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. 

707 

708 .. versionadded:: 0.5.1 

709 """ 

710 with self: 

711 return library.PixelGetYellowQuantum(self.resource) 

712 

713 @yellow_quantum.setter 

714 def yellow_quantum(self, value): 

715 self._assert_quantum(value) 

716 self.dirty = True 

717 with self: 

718 library.PixelSetYellowQuantum(self.resource, value) 

719 

720 def hsl(self): 

721 """Calculate the HSL color values from the RGB color. 

722 

723 :returns: Tuple containing three normalized doubles, between 0.0 & 

724 1.0, representing ``hue``, ``saturation``, and ``lightness``. 

725 :rtype: :class:`collections.Sequence` 

726 

727 .. versionadded:: 0.5.1 

728 """ 

729 hue = ctypes.c_double(0.0) 

730 saturation = ctypes.c_double(0.0) 

731 lightness = ctypes.c_double(0.0) 

732 with self: 

733 library.PixelGetHSL(self.resource, 

734 ctypes.byref(hue), 

735 ctypes.byref(saturation), 

736 ctypes.byref(lightness)) 

737 return (hue.value, saturation.value, lightness.value) 

738 

739 

740def scale_quantum_to_int8(quantum): 

741 """Straightforward port of :c:func:`ScaleQuantumToChar()` inline 

742 function. 

743 

744 :param quantum: quantum value 

745 :type quantum: :class:`numbers.Integral` 

746 :returns: 8bit integer of the given ``quantum`` value 

747 :rtype: :class:`numbers.Integral` 

748 

749 .. versionadded:: 0.3.0 

750 .. versionchanged:: 0.5.0 

751 Added HDRI support 

752 """ 

753 if quantum <= 0: 

754 return 0 

755 table = {8: 1, 16: 257.0, 32: 16843009.0, 64: 72340172838076673.0} 

756 if MAGICK_HDRI: # pragma: no cover 

757 if QUANTUM_DEPTH == 8: 

758 v = quantum / table[QUANTUM_DEPTH] 

759 elif QUANTUM_DEPTH == 16: 

760 v = ((int(quantum + 128) - (int(quantum + 128) >> 8)) >> 8) 

761 elif QUANTUM_DEPTH == 32: 

762 v = ((quantum + 8421504) / table[QUANTUM_DEPTH]) 

763 elif QUANTUM_DEPTH == 64: 

764 v = quantum / table[QUANTUM_DEPTH] 

765 else: 

766 v = quantum / table[QUANTUM_DEPTH] 

767 if v >= 255: 

768 return 255 

769 return int(v + 0.5)